Merge "API changes for addressing API Council comments." into tm-dev
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 2b70bcd..b709693 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -38,10 +38,11 @@
 
 apex {
     name: "com.android.tethering",
-    defaults: ["ConnectivityApexDefaults"],
+    defaults: [
+        "ConnectivityApexDefaults",
+        "r-launched-apex-module",
+    ],
     compile_multilib: "both",
-    updatable: true,
-    min_sdk_version: "30",
     bootclasspath_fragments: [
         "com.android.tethering-bootclasspath-fragment",
     ],
diff --git a/Tethering/src/android/net/ip/DadProxy.java b/Tethering/src/android/net/ip/DadProxy.java
index e2976b7..36ecfe3 100644
--- a/Tethering/src/android/net/ip/DadProxy.java
+++ b/Tethering/src/android/net/ip/DadProxy.java
@@ -16,11 +16,12 @@
 
 package android.net.ip;
 
-import android.net.util.InterfaceParams;
 import android.os.Handler;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.net.module.util.InterfaceParams;
+
 /**
  * Basic Duplicate address detection proxy.
  *
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 2bb19db..acd2625 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -46,7 +46,6 @@
 import android.net.dhcp.IDhcpServer;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -63,11 +62,13 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetdUtils;
 import com.android.networkstack.tethering.BpfCoordinator;
 import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
 import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
 import com.android.networkstack.tethering.PrivateAddressCoordinator;
+import com.android.networkstack.tethering.TetheringConfiguration;
 import com.android.networkstack.tethering.util.InterfaceSet;
 import com.android.networkstack.tethering.util.PrefixUtils;
 
@@ -285,8 +286,8 @@
     public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
             INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
-            boolean usingLegacyDhcp, boolean usingBpfOffload,
-            PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
+            TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
+            Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
@@ -296,8 +297,8 @@
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
-        mUsingLegacyDhcp = usingLegacyDhcp;
-        mUsingBpfOffload = usingBpfOffload;
+        mUsingLegacyDhcp = config.useLegacyDhcpServer();
+        mUsingBpfOffload = config.isBpfOffloadEnabled();
         mPrivateAddressCoordinator = addressCoordinator;
         mDeps = deps;
         resetLinkProperties();
diff --git a/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 27e59a1..723bd63 100644
--- a/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -24,13 +24,13 @@
 import static android.system.OsConstants.SOCK_NONBLOCK;
 import static android.system.OsConstants.SOCK_RAW;
 
-import android.net.util.InterfaceParams;
 import android.net.util.SocketUtils;
 import android.os.Handler;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
 
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.PacketReader;
 import com.android.networkstack.tethering.util.TetheringUtils;
 
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 74f9369..c452e55 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -37,7 +37,6 @@
 import android.net.LinkAddress;
 import android.net.MacAddress;
 import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
 import android.net.util.SocketUtils;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -45,6 +44,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.structs.Icmpv6Header;
 import com.android.net.module.util.structs.LlaOption;
 import com.android.net.module.util.structs.MtuOption;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 6550de2..2ef1d5b 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -44,7 +44,6 @@
 import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
 import android.net.ip.IpServer;
 import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -64,8 +63,9 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
-import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.U32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.netlink.ConntrackMessage;
@@ -1177,22 +1177,13 @@
         }
     }
 
-    /**
-     * Simple struct that only contains a u32. Must be public because Struct needs access to it.
-     * TODO: make this a public inner class of Struct so anyone can use it as, e.g., Struct.U32?
-     */
-    public static class U32Struct extends Struct {
-        @Struct.Field(order = 0, type = Struct.Type.U32)
-        public long val;
-    }
-
     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
         if (!mDeps.isAtLeastS()) {
             pw.println("No counter support");
             return;
         }
-        try (BpfMap<U32Struct, U32Struct> map = new BpfMap<>(TETHER_ERROR_MAP_PATH,
-                BpfMap.BPF_F_RDONLY, U32Struct.class, U32Struct.class)) {
+        try (BpfMap<U32, U32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
+                U32.class, U32.class)) {
 
             map.forEach((k, v) -> {
                 String counterName;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index 77efb51..3d2dfaa 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -20,10 +20,9 @@
 
 import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
 
-import android.net.util.InterfaceParams;
-
 import androidx.annotation.NonNull;
 
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.TcUtils;
 
 import java.io.IOException;
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index c1e7127..cc2422f 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -90,11 +90,8 @@
         mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
         mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
 
-        mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")));
-        if (config.isSelectAllPrefixRangeEnabled()) {
-            mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12"));
-            mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8"));
-        }
+        mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"),
+            new IpPrefix("172.16.0.0/12"), new IpPrefix("10.0.0.0/8")));
     }
 
     /**
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 301a682..bb9b6fb 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1533,16 +1533,28 @@
         return mConfig;
     }
 
-    boolean hasTetherableConfiguration() {
-        final TetheringConfiguration cfg = mConfig;
-        final boolean hasDownstreamConfiguration =
-                (cfg.tetherableUsbRegexs.length != 0)
-                || (cfg.tetherableWifiRegexs.length != 0)
-                || (cfg.tetherableBluetoothRegexs.length != 0);
-        final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty()
-                || cfg.chooseUpstreamAutomatically;
+    boolean hasAnySupportedDownstream() {
+        if ((mConfig.tetherableUsbRegexs.length != 0)
+                || (mConfig.tetherableWifiRegexs.length != 0)
+                || (mConfig.tetherableBluetoothRegexs.length != 0)) {
+            return true;
+        }
 
-        return hasDownstreamConfiguration && hasUpstreamConfiguration;
+        // Before T, isTetheringSupported would return true if wifi, usb and bluetooth tethering are
+        // disabled (whole tethering settings would be hidden). This means tethering would also not
+        // support wifi p2p, ethernet tethering and mirrorlink. This is wrong but probably there are
+        // some devices in the field rely on this to disable tethering entirely.
+        if (!SdkLevel.isAtLeastT()) return false;
+
+        return (mConfig.tetherableWifiP2pRegexs.length != 0)
+                || (mConfig.tetherableNcmRegexs.length != 0)
+                || isEthernetSupported();
+    }
+
+    // TODO: using EtherentManager new API to check whether ethernet is supported when the API is
+    // ready to use.
+    private boolean isEthernetSupported() {
+        return mContext.getSystemService(Context.ETHERNET_SERVICE) != null;
     }
 
     void setUsbTethering(boolean enable, IIntResultListener listener) {
@@ -1745,7 +1757,7 @@
 
             // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
             // Legacy DHCP server is disabled if passed an empty ranges array
-            final String[] dhcpRanges = cfg.enableLegacyDhcpServer
+            final String[] dhcpRanges = cfg.useLegacyDhcpServer()
                     ? cfg.legacyDhcpRanges : new String[0];
             try {
                 NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
@@ -2463,7 +2475,7 @@
         final boolean tetherEnabledInSettings = tetherSupported
                 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
 
-        return tetherEnabledInSettings && hasTetherableConfiguration()
+        return tetherEnabledInSettings && hasAnySupportedDownstream()
                 && !isProvisioningNeededButUnavailable();
     }
 
@@ -2722,8 +2734,7 @@
         mLog.i("adding IpServer for: " + iface);
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
-                             makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator,
+                             makeControlCallback(), mConfig, mPrivateAddressCoordinator,
                              mDeps.getIpServerDependencies()), isNcm);
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index dd0d567..eaf8589 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -99,13 +99,6 @@
             "use_legacy_wifi_p2p_dedicated_ip";
 
     /**
-     * Flag use to enable select all prefix ranges feature.
-     * TODO: Remove this flag if there are no problems after M-2020-12 rolls out.
-     */
-    public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
-            "tether_enable_select_all_prefix_ranges";
-
-    /**
      * Experiment flag to force choosing upstreams automatically.
      *
      * This setting is intended to help force-enable the feature on OEM devices that disabled it
@@ -143,7 +136,6 @@
     public final Collection<Integer> preferredUpstreamIfaceTypes;
     public final String[] legacyDhcpRanges;
     public final String[] defaultIPv4DNS;
-    public final boolean enableLegacyDhcpServer;
 
     public final String[] provisioningApp;
     public final String provisioningAppNoUi;
@@ -152,12 +144,12 @@
 
     public final int activeDataSubId;
 
+    private final boolean mEnableLegacyDhcpServer;
     private final int mOffloadPollInterval;
     // TODO: Add to TetheringConfigurationParcel if required.
     private final boolean mEnableBpfOffload;
     private final boolean mEnableWifiP2pDedicatedIp;
 
-    private final boolean mEnableSelectAllPrefixRange;
     private final int mUsbTetheringFunction;
     protected final ContentResolver mContentResolver;
 
@@ -203,7 +195,7 @@
         legacyDhcpRanges = getLegacyDhcpRanges(res);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
         mEnableBpfOffload = getEnableBpfOffload(res);
-        enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
+        mEnableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
 
         provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
         provisioningAppNoUi = getResourceString(res,
@@ -222,14 +214,14 @@
                 R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
                 false /* defaultValue */);
 
-        // Flags should normally not be booleans, but this is a kill-switch flag that is only used
-        // to turn off the feature, so binary rollback problems do not apply.
-        mEnableSelectAllPrefixRange = getDeviceConfigBoolean(
-                TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */);
-
         configLog.log(toString());
     }
 
+    /** Check whether using legacy dhcp server. */
+    public boolean useLegacyDhcpServer() {
+        return mEnableLegacyDhcpServer;
+    }
+
     /** Check whether using ncm for usb tethering */
     public boolean isUsingNcm() {
         return mUsbTetheringFunction == TETHER_USB_NCM_FUNCTION;
@@ -313,14 +305,11 @@
         pw.println(mEnableBpfOffload);
 
         pw.print("enableLegacyDhcpServer: ");
-        pw.println(enableLegacyDhcpServer);
+        pw.println(mEnableLegacyDhcpServer);
 
         pw.print("enableWifiP2pDedicatedIp: ");
         pw.println(mEnableWifiP2pDedicatedIp);
 
-        pw.print("mEnableSelectAllPrefixRange: ");
-        pw.println(mEnableSelectAllPrefixRange);
-
         pw.print("mUsbTetheringFunction: ");
         pw.println(isUsingNcm() ? "NCM" : "RNDIS");
     }
@@ -342,7 +331,7 @@
         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
         sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
         sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
-        sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
+        sj.add(String.format("enableLegacyDhcpServer:%s", mEnableLegacyDhcpServer));
         return String.format("TetheringConfiguration{%s}", sj.toString());
     }
 
@@ -384,10 +373,6 @@
         return mEnableBpfOffload;
     }
 
-    public boolean isSelectAllPrefixRangeEnabled() {
-        return mEnableSelectAllPrefixRange;
-    }
-
     private int getUsbTetheringFunction(Resources res) {
         final int valueFromRes = getResourceInteger(res, R.integer.config_tether_usb_functions,
                 TETHER_USB_RNDIS_FUNCTION /* defaultValue */);
@@ -596,7 +581,7 @@
 
         parcel.legacyDhcpRanges = legacyDhcpRanges;
         parcel.defaultIPv4DNS = defaultIPv4DNS;
-        parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
+        parcel.enableLegacyDhcpServer = mEnableLegacyDhcpServer;
         parcel.provisioningApp = provisioningApp;
         parcel.provisioningAppNoUi = provisioningAppNoUi;
         parcel.provisioningCheckPeriod = provisioningCheckPeriod;
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index c1a747e..9224213 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -94,13 +94,6 @@
     public abstract IpServer.Dependencies getIpServerDependencies();
 
     /**
-     * Indicates whether tethering is supported on the device.
-     */
-    public boolean isTetheringSupported() {
-        return true;
-    }
-
-    /**
      * Get a reference to the EntitlementManager to be used by tethering.
      */
     public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
index ef254ff..4525568 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -29,7 +29,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 
 import android.app.UiAutomation;
 import android.content.Context;
@@ -81,12 +80,8 @@
         mUiAutomation.dropShellPermissionIdentity();
     }
 
-    private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
-            "tether_enable_select_all_prefix_ranges";
     @Test
     public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
-        assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true));
-
         addressConflictTest(true);
     }
 
diff --git a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index eb9cf71..ebf09ed 100644
--- a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -30,7 +30,6 @@
 import android.net.INetd;
 import android.net.InetAddresses;
 import android.net.MacAddress;
-import android.net.util.InterfaceParams;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -40,6 +39,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.net.module.util.InterfaceParams;
 import com.android.networkstack.tethering.util.TetheringUtils;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
index 34f3e0e..328e3fb 100644
--- a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
@@ -44,7 +44,6 @@
 import android.net.MacAddress;
 import android.net.RouteInfo;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.util.InterfaceParams;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -54,6 +53,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.Ipv6Utils;
 import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.Struct;
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index a3c46c2..6488421 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -86,7 +86,6 @@
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Build;
 import android.os.Handler;
@@ -100,6 +99,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
@@ -228,9 +228,11 @@
         doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
                 neighborCaptor.capture());
 
+        when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(usingBpfOffload);
+        when(mTetherConfig.useLegacyDhcpServer()).thenReturn(usingLegacyDhcp);
         mIpServer = new IpServer(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
-                mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
+                mCallback, mTetherConfig, mAddressCoordinator, mDependencies);
         mIpServer.start();
         mNeighborEventConsumer = neighborCaptor.getValue();
 
@@ -281,7 +283,8 @@
         when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
         when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
                 mTestAddress);
-        when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
+        when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
+        when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
 
         mBpfDeps = new BpfCoordinator.Dependencies() {
                     @NonNull
@@ -360,8 +363,8 @@
         when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
                 .thenReturn(mIpNeighborMonitor);
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
-                mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */,
-                DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies);
+                mNetd, mBpfCoordinator, mCallback, mTetherConfig, mAddressCoordinator,
+                mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 6c7a66d..c614046 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -84,7 +84,6 @@
 import android.net.ip.ConntrackMonitor;
 import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
 import android.net.ip.IpServer;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Build;
 import android.os.Handler;
@@ -98,6 +97,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 6c98f2f..55d9852 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -100,7 +100,6 @@
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
         when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
-        when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true);
         setUpIpServers();
         mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
     }
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 c0c2ab9..e8bb315 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -95,7 +95,6 @@
     @Mock private ModuleInfo mMi;
     private Context mMockContext;
     private boolean mHasTelephonyManager;
-    private boolean mEnableLegacyDhcpServer;
     private MockitoSession mMockingSession;
     private MockContentResolver mContentResolver;
 
@@ -182,11 +181,9 @@
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
                 .thenReturn(false);
         initializeBpfOffloadConfiguration(true, null /* unset */);
-        initEnableSelectAllPrefixRangeFlag(null /* unset */);
 
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
-        mEnableLegacyDhcpServer = false;
 
         mContentResolver = new MockContentResolver(mMockContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -398,7 +395,7 @@
 
         final TetheringConfiguration enableByRes =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertTrue(enableByRes.enableLegacyDhcpServer);
+        assertTrue(enableByRes.useLegacyDhcpServer());
 
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
@@ -408,7 +405,7 @@
 
         final TetheringConfiguration enableByDevConfig =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertTrue(enableByDevConfig.enableLegacyDhcpServer);
+        assertTrue(enableByDevConfig.useLegacyDhcpServer());
     }
 
     @Test
@@ -422,7 +419,7 @@
         final TetheringConfiguration cfg =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
 
-        assertFalse(cfg.enableLegacyDhcpServer);
+        assertFalse(cfg.useLegacyDhcpServer());
     }
 
     @Test
@@ -490,32 +487,6 @@
         assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
     }
 
-    private void initEnableSelectAllPrefixRangeFlag(final String value) {
-        doReturn(value).when(
-                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES)));
-    }
-
-    @Test
-    public void testSelectAllPrefixRangeFlag() throws Exception {
-        // Test default value.
-        final TetheringConfiguration defaultCfg = new TetheringConfiguration(
-                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled());
-
-        // Test disable flag.
-        initEnableSelectAllPrefixRangeFlag("false");
-        final TetheringConfiguration testDisable = new TetheringConfiguration(
-                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertFalse(testDisable.isSelectAllPrefixRangeEnabled());
-
-        // Test enable flag.
-        initEnableSelectAllPrefixRangeFlag("true");
-        final TetheringConfiguration testEnable = new TetheringConfiguration(
-                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-        assertTrue(testEnable.isSelectAllPrefixRangeEnabled());
-    }
-
     @Test
     public void testChooseUpstreamAutomatically() throws Exception {
         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index e4dbc7d..0388758 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -152,7 +152,6 @@
 import android.net.ip.IpNeighborMonitor;
 import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
 import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
 import android.net.wifi.SoftApConfiguration;
@@ -185,6 +184,7 @@
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.InterfaceParams;
 import com.android.networkstack.apishim.common.BluetoothPanShim;
 import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
 import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
@@ -297,6 +297,7 @@
     private TetheredInterfaceCallbackShim mTetheredInterfaceCallbackShim;
 
     private TestConnectivityManager mCm;
+    private boolean mForceEthernetServiceUnavailable = false;
 
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
@@ -331,7 +332,11 @@
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
-            if (Context.ETHERNET_SERVICE.equals(name)) return mEm;
+            if (Context.ETHERNET_SERVICE.equals(name)) {
+                if (mForceEthernetServiceUnavailable) return null;
+
+                return mEm;
+            }
             return super.getSystemService(name);
         }
 
@@ -452,11 +457,6 @@
         }
 
         @Override
-        public boolean isTetheringSupported() {
-            return true;
-        }
-
-        @Override
         public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
                 int subId) {
             mConfig = spy(new FakeTetheringConfiguration(ctx, log, subId));
@@ -680,6 +680,7 @@
                 .thenReturn(new String[] {TEST_BT_REGEX});
         when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
                 .thenReturn(new String[] {TEST_NCM_REGEX});
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)).thenReturn(true);
         when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
                 new int[] {TYPE_WIFI, TYPE_MOBILE_DUN});
         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
@@ -2834,6 +2835,55 @@
         runDualStackUsbTethering(TEST_RNDIS_IFNAME);
         runStopUSBTethering();
     }
+
+    @Test
+    public void testTetheringSupported() throws Exception {
+        setTetheringSupported(true /* supported */);
+        updateConfigAndVerifySupported(true /* supported */);
+
+        // Could disable tethering supported by settings.
+        Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, 0);
+        updateConfigAndVerifySupported(false /* supported */);
+
+        // Could disable tethering supported by user restriction.
+        setTetheringSupported(true /* supported */);
+        when(mUserManager.hasUserRestriction(
+                UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(true);
+        updateConfigAndVerifySupported(false /* supported */);
+
+        // Tethering is supported if it has any supported downstream.
+        setTetheringSupported(true /* supported */);
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+                .thenReturn(new String[0]);
+        updateConfigAndVerifySupported(true /* supported */);
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+                .thenReturn(new String[0]);
+        updateConfigAndVerifySupported(true /* supported */);
+
+
+        if (isAtLeastT()) {
+            when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+                    .thenReturn(new String[0]);
+            updateConfigAndVerifySupported(true /* supported */);
+            when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
+                    .thenReturn(new String[0]);
+            updateConfigAndVerifySupported(true /* supported */);
+            when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
+                    .thenReturn(new String[0]);
+            updateConfigAndVerifySupported(true /* supported */);
+            mForceEthernetServiceUnavailable = true;
+            updateConfigAndVerifySupported(false /* supported */);
+        } else {
+            when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+                    .thenReturn(new String[0]);
+            updateConfigAndVerifySupported(false /* supported */);
+        }
+    }
+
+    private void updateConfigAndVerifySupported(boolean supported) {
+        sendConfigurationChanged();
+        assertEquals(supported, mTethering.isTetheringSupported());
+    }
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/ethernet/Android.bp b/ethernet/Android.bp
new file mode 100644
index 0000000..36978c5
--- /dev/null
+++ b/ethernet/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+// TODO: delete once:
+// - The ethernet code has been moved to p/m/C.
+// - All dependencies in the tree are gone.
+java_library {
+    name: "ethernet-service",
+    installable: true,
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
diff --git a/ethernet/src/com/android/server/ethernet/stub/EthernetService.java b/ethernet/src/com/android/server/ethernet/stub/EthernetService.java
new file mode 100644
index 0000000..d0010c1
--- /dev/null
+++ b/ethernet/src/com/android/server/ethernet/stub/EthernetService.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server.ethernet.stub;
+
+import android.content.Context;
+import android.os.Binder;
+
+/**
+ * Fake EthernetService class for branches that do not have the updatable EthernetService yet,
+ * to allow building the T service-connectivity before sources are moved to the branch.
+ * TODO: Once ethernet-service is no longer on the system server
+ * classpath, move these files from com.android.server.ethernet.stub
+ * to com.android.server.ethernet.
+ */
+public final class EthernetService extends Binder {
+    /** Create instance */
+    public static EthernetServiceImpl create(Context ctx) {
+        throw new RuntimeException("This is a stub class");
+    }
+}
diff --git a/ethernet/src/com/android/server/ethernet/stub/EthernetServiceImpl.java b/ethernet/src/com/android/server/ethernet/stub/EthernetServiceImpl.java
new file mode 100644
index 0000000..ff7aaf9
--- /dev/null
+++ b/ethernet/src/com/android/server/ethernet/stub/EthernetServiceImpl.java
@@ -0,0 +1,25 @@
+/*
+ * 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.server.ethernet.stub;
+
+/** Stub class for EthernetServiceImpl */
+public class EthernetServiceImpl {
+    /** Start service */
+    public void start() {
+        throw new RuntimeException("This is a stub class");
+    }
+}
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index 2ab69b8..f46d887 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -127,6 +127,7 @@
         "//frameworks/base/tests/vcn",
         "//frameworks/libs/net/common/testutils",
         "//frameworks/libs/net/common/tests:__subpackages__",
+        "//frameworks/opt/net/ethernet/tests:__subpackages__",
         "//frameworks/opt/telephony/tests/telephonytests",
         "//packages/modules/CaptivePortalLogin/tests",
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index 3d6fb3e..4fefa0a 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -61,6 +61,14 @@
 
 package android.net {
 
+  public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    ctor public EthernetNetworkSpecifier(@NonNull String);
+    method public int describeContents();
+    method @Nullable public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR;
+  }
+
   public final class IpSecAlgorithm implements android.os.Parcelable {
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]);
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int);
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 59ca730..3eca521 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -31,6 +31,22 @@
     method public static void registerServiceWrappers();
   }
 
+  public class EthernetManager {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void addInterfaceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.InterfaceStateListener);
+    method public void removeInterfaceStateListener(@NonNull android.net.EthernetManager.InterfaceStateListener);
+    method public void setIncludeTestInterfaces(boolean);
+    field public static final int ROLE_CLIENT = 1; // 0x1
+    field public static final int ROLE_NONE = 0; // 0x0
+    field public static final int ROLE_SERVER = 2; // 0x2
+    field public static final int STATE_ABSENT = 0; // 0x0
+    field public static final int STATE_LINK_DOWN = 1; // 0x1
+    field public static final int STATE_LINK_UP = 2; // 0x2
+  }
+
+  public static interface EthernetManager.InterfaceStateListener {
+    method public void onInterfaceStateChanged(@NonNull String, int, int, @Nullable android.net.IpConfiguration);
+  }
+
   public class IpSecManager {
     field public static final int DIRECTION_FWD = 2; // 0x2
   }
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 257ca8a..4739d8d 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -71,21 +71,21 @@
     method @NonNull public android.nearby.FastPairAccountKeyDeviceMetadata.Builder setSha256AccountKeyPublicAddress(@Nullable byte[]);
   }
 
-  public class FastPairAntispoofkeyDeviceMetadata {
+  public class FastPairAntispoofKeyDeviceMetadata {
   }
 
-  public static final class FastPairAntispoofkeyDeviceMetadata.Builder {
-    ctor public FastPairAntispoofkeyDeviceMetadata.Builder();
-    method @NonNull public android.nearby.FastPairAntispoofkeyDeviceMetadata build();
-    method @NonNull public android.nearby.FastPairAntispoofkeyDeviceMetadata.Builder setAntiSpoofPublicKey(@Nullable byte[]);
-    method @NonNull public android.nearby.FastPairAntispoofkeyDeviceMetadata.Builder setFastPairDeviceMetadata(@Nullable android.nearby.FastPairDeviceMetadata);
+  public static final class FastPairAntispoofKeyDeviceMetadata.Builder {
+    ctor public FastPairAntispoofKeyDeviceMetadata.Builder();
+    method @NonNull public android.nearby.FastPairAntispoofKeyDeviceMetadata build();
+    method @NonNull public android.nearby.FastPairAntispoofKeyDeviceMetadata.Builder setAntiSpoofPublicKey(@Nullable byte[]);
+    method @NonNull public android.nearby.FastPairAntispoofKeyDeviceMetadata.Builder setFastPairDeviceMetadata(@Nullable android.nearby.FastPairDeviceMetadata);
   }
 
   public abstract class FastPairDataProviderBase {
     ctor public FastPairDataProviderBase(@NonNull String);
     method @Nullable public final android.os.IBinder getBinder();
     method public abstract void onLoadFastPairAccountDevicesMetadata(@NonNull android.nearby.FastPairDataProviderBase.FastPairAccountDevicesMetadataRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairAccountDevicesMetadataCallback);
-    method public abstract void onLoadFastPairAntispoofkeyDeviceMetadata(@NonNull android.nearby.FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataCallback);
+    method public abstract void onLoadFastPairAntispoofKeyDeviceMetadata(@NonNull android.nearby.FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataCallback);
     method public abstract void onLoadFastPairEligibleAccounts(@NonNull android.nearby.FastPairDataProviderBase.FastPairEligibleAccountsRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairEligibleAccountsCallback);
     method public abstract void onManageFastPairAccount(@NonNull android.nearby.FastPairDataProviderBase.FastPairManageAccountRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairManageActionCallback);
     method public abstract void onManageFastPairAccountDevice(@NonNull android.nearby.FastPairDataProviderBase.FastPairManageAccountDeviceRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairManageActionCallback);
@@ -106,12 +106,12 @@
     method @NonNull public java.util.Collection<byte[]> getAccountKeys();
   }
 
-  public static interface FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataCallback {
+  public static interface FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataCallback {
     method public void onError(int, @Nullable String);
-    method public void onFastPairAntispoofkeyDeviceMetadataReceived(@NonNull android.nearby.FastPairAntispoofkeyDeviceMetadata);
+    method public void onFastPairAntispoofKeyDeviceMetadataReceived(@NonNull android.nearby.FastPairAntispoofKeyDeviceMetadata);
   }
 
-  public static class FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest {
+  public static class FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest {
     method @NonNull public byte[] getModelId();
   }
 
@@ -329,6 +329,8 @@
   }
 
   public class NearbyManager {
+    method public static boolean getFastPairScanEnabled(@NonNull android.content.Context, boolean);
+    method public static void setFastPairScanEnabled(@NonNull android.content.Context, boolean);
     method public void startBroadcast(@NonNull android.nearby.BroadcastRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.BroadcastCallback);
     method public void startScan(@NonNull android.nearby.ScanRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.ScanCallback);
     method public void stopBroadcast(@NonNull android.nearby.BroadcastCallback);
@@ -494,6 +496,45 @@
 
 package android.net {
 
+  public class EthernetManager {
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void connectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disconnectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration(@NonNull String, @NonNull android.net.EthernetNetworkUpdateRequest, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+  }
+
+  public static interface EthernetManager.TetheredInterfaceCallback {
+    method public void onAvailable(@NonNull String);
+    method public void onUnavailable();
+  }
+
+  public static class EthernetManager.TetheredInterfaceRequest {
+    method public void release();
+  }
+
+  public final class EthernetNetworkManagementException extends java.lang.RuntimeException implements android.os.Parcelable {
+    ctor public EthernetNetworkManagementException(@NonNull String);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkManagementException> CREATOR;
+  }
+
+  public final class EthernetNetworkUpdateRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.IpConfiguration getIpConfiguration();
+    method @Nullable public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR;
+  }
+
+  public static final class EthernetNetworkUpdateRequest.Builder {
+    ctor public EthernetNetworkUpdateRequest.Builder();
+    ctor public EthernetNetworkUpdateRequest.Builder(@NonNull android.net.EthernetNetworkUpdateRequest);
+    method @NonNull public android.net.EthernetNetworkUpdateRequest build();
+    method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setIpConfiguration(@NonNull android.net.IpConfiguration);
+    method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+  }
+
   public class IpSecManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
diff --git a/framework/Android.bp b/framework/Android.bp
index 66b662b..f31a7d5 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -64,6 +64,7 @@
         ":framework-connectivity-sources",
         ":net-utils-framework-common-srcs",
         ":framework-connectivity-api-shared-srcs",
+        ":framework-connectivity-javastream-protos",
     ],
     aidl: {
         generate_get_transaction_name: true,
@@ -85,7 +86,7 @@
         "net-utils-device-common",
     ],
     static_libs: [
-        "framework-connectivity-protos",
+        "modules-utils-backgroundthread",
         "modules-utils-build",
         "modules-utils-preconditions",
     ],
@@ -139,6 +140,7 @@
         "//frameworks/base/tests/vcn",
         "//frameworks/libs/net/common/testutils",
         "//frameworks/libs/net/common/tests:__subpackages__",
+        "//frameworks/opt/net/ethernet/tests:__subpackages__",
         "//frameworks/opt/telephony/tests/telephonytests",
         "//packages/modules/CaptivePortalLogin/tests",
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
@@ -180,22 +182,36 @@
     ],
 }
 
-// TODO: reduce size of this library; consider using
-// proto nano for example
-java_library {
+filegroup {
     name: "framework-connectivity-protos",
-    sdk_version: "module_current",
-    min_sdk_version: "30",
-    proto: {
-        type: "lite",
-    },
     srcs: [
-        "proto/**/*.*",
+        "proto/**/*.proto",
     ],
-   static_libs: ["libprotobuf-java-lite"],
-    apex_available: [
-        "com.android.tethering",
+    visibility: ["//frameworks/base"],
+}
+
+gensrcs {
+    name: "framework-connectivity-javastream-protos",
+    depfile: true,
+
+    tools: [
+        "aprotoc",
+        "protoc-gen-javastream",
+        "soong_zip",
     ],
-    lint: { strict_updatability_linting: true },
-    visibility: ["//visibility:private"],
+
+    cmd: "mkdir -p $(genDir)/$(in) " +
+        "&& $(location aprotoc) " +
+        "  --plugin=$(location protoc-gen-javastream) " +
+        "  --dependency_out=$(depfile) " +
+        "  --javastream_out=$(genDir)/$(in) " +
+        "  -Iexternal/protobuf/src " +
+        "  -I . " +
+        "  $(in) " +
+        "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+    srcs: [
+        ":framework-connectivity-protos",
+    ],
+    output_extension: "srcjar",
 }
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index b65c0ce..9fe6505 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -6,6 +6,8 @@
   }
 
   public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void addUidToMeteredNetworkAllowList(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void addUidToMeteredNetworkDenyList(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset();
     method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshots();
     method @Nullable public android.net.ProxyInfo getGlobalProxy();
@@ -14,6 +16,8 @@
     method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.NetworkCapabilities redactNetworkCapabilitiesForPackage(@NonNull android.net.NetworkCapabilities, int, @NonNull String);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkAllowList(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkDenyList(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void replaceFirewallChain(int, @NonNull int[]);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
@@ -28,11 +32,8 @@
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void swapActiveStatsMap();
     method public void systemReady();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateFirewallRule(int, int, boolean);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateMeteredNetworkAllowList(int, boolean);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateMeteredNetworkDenyList(int, boolean);
     field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
     field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
     field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index e8e1efa..68ca46d 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -5706,8 +5706,8 @@
     }
 
     /**
-     * Sets whether the specified UID is allowed to use data on metered networks even when
-     * background data is restricted.
+     * Adds the specified UID to the list of UIds that are allowed to use data on metered networks
+     * even when background data is restricted. The deny list takes precedence over the allow list.
      *
      * @param uid uid of target app
      * @throws IllegalStateException if updating allow list failed.
@@ -5719,17 +5719,40 @@
             android.Manifest.permission.NETWORK_STACK,
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
-    public void updateMeteredNetworkAllowList(final int uid, final boolean add) {
+    public void addUidToMeteredNetworkAllowList(final int uid) {
         try {
-            mService.updateMeteredNetworkAllowList(uid, add);
+            mService.updateMeteredNetworkAllowList(uid, true /* add */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Sets whether the specified UID is prevented from using background data on metered networks.
-     * Takes precedence over {@link #updateMeteredNetworkAllowList}.
+     * Removes the specified UID from the list of UIDs that are allowed to use background data on
+     * metered networks when background data is restricted. The deny list takes precedence over
+     * the allow list.
+     *
+     * @param uid uid of target app
+     * @throws IllegalStateException if updating allow list failed.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
+    public void removeUidFromMeteredNetworkAllowList(final int uid) {
+        try {
+            mService.updateMeteredNetworkAllowList(uid, false /* remove */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adds the specified UID to the list of UIDs that are not allowed to use background data on
+     * metered networks. Takes precedence over {@link #addUidToMeteredNetworkAllowList}.
      *
      * @param uid uid of target app
      * @throws IllegalStateException if updating deny list failed.
@@ -5741,9 +5764,32 @@
             android.Manifest.permission.NETWORK_STACK,
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
-    public void updateMeteredNetworkDenyList(final int uid, final boolean add) {
+    public void addUidToMeteredNetworkDenyList(final int uid) {
         try {
-            mService.updateMeteredNetworkDenyList(uid, add);
+            mService.updateMeteredNetworkDenyList(uid, true /* add */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes the specified UID from the list of UIds that can use use background data on metered
+     * networks if background data is not restricted. The deny list takes precedence over the
+     * allow list.
+     *
+     * @param uid uid of target app
+     * @throws IllegalStateException if updating deny list failed.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
+    public void removeUidFromMeteredNetworkDenyList(final int uid) {
+        try {
+            mService.updateMeteredNetworkDenyList(uid, false /* remove */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -5818,27 +5864,4 @@
             throw e.rethrowFromSystemServer();
         }
     }
-
-    /**
-     * Request to change the current active network stats map.
-     * STOPSHIP: Remove this API before T sdk finalized, this API is temporary added for the
-     * NetworkStatsFactory which is platform code but will be moved into connectivity (tethering)
-     * mainline module.
-     *
-     * @throws IllegalStateException if swapping active stats map failed.
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.NETWORK_SETTINGS,
-            android.Manifest.permission.NETWORK_STACK,
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
-    })
-    public void swapActiveStatsMap() {
-        try {
-            mService.swapActiveStatsMap();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
 }
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 23a3850..1e1f653 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -245,6 +245,4 @@
     void setFirewallChainEnabled(int chain, boolean enable);
 
     void replaceFirewallChain(int chain, in int[] uids);
-
-    void swapActiveStatsMap();
 }
diff --git a/framework/src/android/net/QosFilter.java b/framework/src/android/net/QosFilter.java
index 957c867..5c1c3cc 100644
--- a/framework/src/android/net/QosFilter.java
+++ b/framework/src/android/net/QosFilter.java
@@ -62,23 +62,31 @@
     public abstract int validate();
 
     /**
-     * Determines whether or not the parameters is a match for the filter.
+     * Determines whether or not the parameters will be matched with source address and port of this
+     * filter.
      *
-     * @param address the local address
-     * @param startPort the start of the port range
-     * @param endPort the end of the port range
-     * @return whether the parameters match the local address of the filter
+     * @param address the UE side address included in IP packet filter set of a QoS flow assigned
+     *                on {@link Network}.
+     * @param startPort the start of UE side port range included in IP packet filter set of a QoS
+     *                flow assigned on {@link Network}.
+     * @param endPort the end of UE side port range included in IP packet filter set of a QoS flow
+     *                assigned on {@link Network}.
+     * @return whether the parameters match the UE side address and port of the filter
      */
     public abstract boolean matchesLocalAddress(@NonNull InetAddress address,
             int startPort, int endPort);
 
     /**
-     * Determines whether or not the parameters is a match for the filter.
+     * Determines whether or not the parameters will be matched with remote address and port of
+     * this filter.
      *
-     * @param address the remote address
-     * @param startPort the start of the port range
-     * @param endPort the end of the port range
-     * @return whether the parameters match the remote address of the filter
+     * @param address the remote address included in IP packet filter set of a QoS flow
+     *                assigned on {@link Network}.
+     * @param startPort the start of remote port range included in IP packet filter set of a
+     *                 QoS flow assigned on {@link Network}.
+     * @param endPort the end of the remote range included in IP packet filter set of a QoS
+     *                flow assigned on {@link Network}.
+     * @return whether the parameters match the remote address and port of the filter
      */
     public abstract boolean matchesRemoteAddress(@NonNull InetAddress address,
             int startPort, int endPort);
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 3cd5e13..f3dfb57 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -134,6 +134,46 @@
         return -EPERM;
     }
 
+    // Note that tagging the socket to AID_CLAT is only implemented in JNI ClatCoordinator.
+    // The process is not allowed to tag socket to AID_CLAT via tagSocket() which would cause
+    // process data usage accounting to be bypassed. Tagging AID_CLAT is used for avoiding counting
+    // CLAT traffic data usage twice. See packages/modules/Connectivity/service/jni/
+    // com_android_server_connectivity_ClatCoordinator.cpp
+    if (chargeUid == AID_CLAT) {
+        return -EPERM;
+    }
+
+    // The socket destroy listener only monitors on the group {INET_TCP, INET_UDP, INET6_TCP,
+    // INET6_UDP}. Tagging listener unsupported socket causes that the tag can't be removed from
+    // tag map automatically. Eventually, the tag map may run out of space because of dead tag
+    // entries. Note that although tagSocket() of net client has already denied the family which
+    // is neither AF_INET nor AF_INET6, the family validation is still added here just in case.
+    // See tagSocket in system/netd/client/NetdClient.cpp and
+    // TrafficController::makeSkDestroyListener in
+    // packages/modules/Connectivity/service/native/TrafficController.cpp
+    // TODO: remove this once the socket destroy listener can detect more types of socket destroy.
+    int socketFamily;
+    socklen_t familyLen = sizeof(socketFamily);
+    if (getsockopt(sockFd, SOL_SOCKET, SO_DOMAIN, &socketFamily, &familyLen)) {
+        ALOGE("Failed to getsockopt SO_DOMAIN: %s, fd: %d", strerror(errno), sockFd);
+        return -errno;
+    }
+    if (socketFamily != AF_INET && socketFamily != AF_INET6) {
+        ALOGE("Unsupported family: %d", socketFamily);
+        return -EAFNOSUPPORT;
+    }
+
+    int socketProto;
+    socklen_t protoLen = sizeof(socketProto);
+    if (getsockopt(sockFd, SOL_SOCKET, SO_PROTOCOL, &socketProto, &protoLen)) {
+        ALOGE("Failed to getsockopt SO_PROTOCOL: %s, fd: %d", strerror(errno), sockFd);
+        return -errno;
+    }
+    if (socketProto != IPPROTO_UDP && socketProto != IPPROTO_TCP) {
+        ALOGE("Unsupported protocol: %d", socketProto);
+        return -EPROTONOSUPPORT;
+    }
+
     uint64_t sock_cookie = getSocketCookie(sockFd);
     if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
     UidTagValue newKey = {.uid = (uint32_t)chargeUid, .tag = tag};
diff --git a/netd/BpfHandlerTest.cpp b/netd/BpfHandlerTest.cpp
index db59c7c..cd6b565 100644
--- a/netd/BpfHandlerTest.cpp
+++ b/netd/BpfHandlerTest.cpp
@@ -16,6 +16,7 @@
  * BpfHandlerTest.cpp - unit tests for BpfHandler.cpp
  */
 
+#include <private/android_filesystem_config.h>
 #include <sys/socket.h>
 
 #include <gtest/gtest.h>
@@ -187,6 +188,20 @@
     expectMapEmpty(mFakeCookieTagMap);
 }
 
+TEST_F(BpfHandlerTest, TestTagSocketWithUnsupportedFamily) {
+    int packetSocket = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+    EXPECT_LE(0, packetSocket);
+    EXPECT_NE(NONEXISTENT_COOKIE, getSocketCookie(packetSocket));
+    EXPECT_EQ(-EAFNOSUPPORT, mBh.tagSocket(packetSocket, TEST_TAG, TEST_UID, TEST_UID));
+}
+
+TEST_F(BpfHandlerTest, TestTagSocketWithUnsupportedProtocol) {
+    int rawSocket = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+    EXPECT_LE(0, rawSocket);
+    EXPECT_NE(NONEXISTENT_COOKIE, getSocketCookie(rawSocket));
+    EXPECT_EQ(-EPROTONOSUPPORT, mBh.tagSocket(rawSocket, TEST_TAG, TEST_UID, TEST_UID));
+}
+
 TEST_F(BpfHandlerTest, TestTagSocketWithoutPermission) {
     int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
     ASSERT_NE(-1, sock);
@@ -208,6 +223,12 @@
     EXPECT_EQ(0, mBh.untagSocket(v6socket));
     expectNoTag(sockCookie);
     expectMapEmpty(mFakeCookieTagMap);
+
+    // Tag a socket to AID_CLAT other then realUid.
+    int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+    ASSERT_NE(-1, sock);
+    ASSERT_EQ(-EPERM, mBh.tagSocket(sock, TEST_TAG, AID_CLAT, realUid));
+    expectMapEmpty(mFakeCookieTagMap);
 }
 
 TEST_F(BpfHandlerTest, TestUntagInvalidSocket) {
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 3d9820d..8851554 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -30,6 +30,7 @@
     min_sdk_version: "30",
     srcs: [
         "src/**/*.java",
+        ":ethernet-service-updatable-sources",
         ":services.connectivity-tiramisu-updatable-sources",
     ],
     libs: [
@@ -38,7 +39,8 @@
         "framework-connectivity-t-pre-jarjar",
         "framework-tethering.stubs.module_lib",
         "service-connectivity-pre-jarjar",
-        "service-nearby",
+        "service-nearby-pre-jarjar",
+        "ServiceConnectivityResources",
         "unsupportedappusage",
     ],
     static_libs: [
diff --git a/service-t/src/com/android/server/ConnectivityServiceInitializer.java b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
index d11f0f2..7b1cd61 100644
--- a/service-t/src/com/android/server/ConnectivityServiceInitializer.java
+++ b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
@@ -20,6 +20,9 @@
 import android.util.Log;
 
 import com.android.modules.utils.build.SdkLevel;
+import com.android.networkstack.apishim.ConstantsShim;
+import com.android.server.ethernet.EthernetService;
+import com.android.server.ethernet.EthernetServiceImpl;
 import com.android.server.nearby.NearbyService;
 
 /**
@@ -28,15 +31,19 @@
  */
 public final class ConnectivityServiceInitializer extends SystemService {
     private static final String TAG = ConnectivityServiceInitializer.class.getSimpleName();
+    private final Context mContext;
     private final ConnectivityService mConnectivity;
     private final IpSecService mIpSecService;
     private final NsdService mNsdService;
     private final NearbyService mNearbyService;
+    private final EthernetServiceImpl mEthernetServiceImpl;
 
     public ConnectivityServiceInitializer(Context context) {
         super(context);
+        mContext = context;
         // Load JNI libraries used by ConnectivityService and its dependencies
         System.loadLibrary("service-connectivity");
+        mEthernetServiceImpl = createEthernetService(context);
         mConnectivity = new ConnectivityService(context);
         mIpSecService = createIpSecService(context);
         mNsdService = createNsdService(context);
@@ -45,6 +52,12 @@
 
     @Override
     public void onStart() {
+        if (mConnectivity.deviceSupportsEthernet(mContext)) {
+            Log.i(TAG, "Registering " + Context.ETHERNET_SERVICE);
+            publishBinderService(Context.ETHERNET_SERVICE, mEthernetServiceImpl,
+                    /* allowIsolated= */ false);
+        }
+
         Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
         publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
                 /* allowIsolated= */ false);
@@ -60,10 +73,11 @@
         }
 
         if (mNearbyService != null) {
-            Log.i(TAG, "Registering " + Context.NEARBY_SERVICE);
-            publishBinderService(Context.NEARBY_SERVICE, mNearbyService,
+            Log.i(TAG, "Registering " + ConstantsShim.NEARBY_SERVICE);
+            publishBinderService(ConstantsShim.NEARBY_SERVICE, mNearbyService,
                     /* allowIsolated= */ false);
         }
+
     }
 
     @Override
@@ -71,6 +85,10 @@
         if (mNearbyService != null) {
             mNearbyService.onBootPhase(phase);
         }
+
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && mEthernetServiceImpl != null) {
+            mEthernetServiceImpl.start();
+        }
     }
 
     /**
@@ -96,6 +114,24 @@
     /** Return Nearby service instance or null if current SDK is lower than T */
     private NearbyService createNearbyService(final Context context) {
         if (!SdkLevel.isAtLeastT()) return null;
-        return new NearbyService(context);
+        try {
+            return new NearbyService(context);
+        } catch (UnsupportedOperationException e) {
+            // Nearby is not yet supported in all branches
+            // TODO: remove catch clause when it is available.
+            Log.i(TAG, "Skipping unsupported service " + ConstantsShim.NEARBY_SERVICE);
+            return null;
+        }
+    }
+
+    /**
+     * Return EthernetServiceImpl instance or null if current SDK is lower than T or Ethernet
+     * service isn't necessary.
+     */
+    private EthernetServiceImpl createEthernetService(final Context context) {
+        if (!SdkLevel.isAtLeastT() || !mConnectivity.deviceSupportsEthernet(context)) {
+            return null;
+        }
+        return EthernetService.create(context);
     }
 }
diff --git a/service/Android.bp b/service/Android.bp
index 3e9ef1a..0b8b25f 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -114,6 +114,7 @@
         "net-utils-device-common",
         "net-utils-device-common-bpf",
         "net-utils-device-common-netlink",
+        "net-utils-services-common",
         "netd-client",
         "networkstack-client",
         "PlatformProperties",
@@ -159,7 +160,7 @@
     static_libs: [
         "service-connectivity-pre-jarjar",
         "service-connectivity-tiramisu-pre-jarjar",
-        "service-nearby",
+        "service-nearby-pre-jarjar",
     ],
     jarjar_rules: ":connectivity-jarjar-rules",
     apex_available: [
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index f8f86a2..faa9998 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -136,4 +136,36 @@
          extreme adverse effects on performance of the new network.
     -->
     <bool translatable="false" name="config_cellular_radio_timesharing_capable">true</bool>
+
+    <!-- Configure ethernet tcp buffersizes in the form:
+         rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max -->
+    <string translatable="false" name="config_ethernet_tcp_buffers">524288,1048576,3145728,524288,1048576,2097152</string>
+
+    <!-- Configuration of Ethernet interfaces in the following format:
+         <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]
+         Where
+               [Network Capabilities] Optional. A comma separated list of network capabilities.
+                   Values must be from NetworkCapabilities#NET_CAPABILITY_* constants.
+                   The NOT_ROAMING, NOT_CONGESTED and NOT_SUSPENDED capabilities are always
+                   added automatically because this configuration provides no way to update
+                   them dynamically.
+               [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
+                   use the following format to specify static IP configuration:
+                       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
+                       domains=<comma-sep-domains>
+               [Override Transport] Optional. An override network transport type to allow
+                    the propagation of an interface type on the other end of a local Ethernet
+                    interface. Value must be from NetworkCapabilities#TRANSPORT_* constants. If
+                    left out, this will default to TRANSPORT_ETHERNET.
+         -->
+    <string-array translatable="false" name="config_ethernet_interfaces">
+        <!--
+        <item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item>
+        <item>eth2;;ip=192.168.0.11/24</item>
+        <item>eth3;12,13,14,15;ip=192.168.0.12/24;1</item>
+        -->
+    </string-array>
+
+    <!-- Regex of wired ethernet ifaces -->
+    <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
 </resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index e5010d7..9fa6a30 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -37,6 +37,9 @@
             <item type="drawable" name="stat_notify_wifi_in_range"/>
             <item type="drawable" name="stat_notify_rssi_in_range"/>
             <item type="bool" name="config_cellular_radio_timesharing_capable" />
+            <item type="string" name="config_ethernet_tcp_buffers"/>
+            <item type="array" name="config_ethernet_interfaces"/>
+            <item type="string" name="config_ethernet_iface_regex"/>
         </policy>
     </overlayable>
 </resources>
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index ee05df5..5d51fdd 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -92,7 +92,6 @@
 rule android.net.util.KeepalivePacketDataUtil* com.android.connectivity.@0
 
 # From connectivity-module-utils
-rule android.net.util.InterfaceParams* com.android.connectivity.@0
 rule android.net.util.SharedLog* com.android.connectivity.@0
 rule android.net.shared.** com.android.connectivity.@0
 
@@ -109,5 +108,8 @@
 # From fast-pair-lite-protos
 rule service.proto.** com.android.server.nearby.@0
 
+# From filegroup framework-connectivity-protos
+rule android.service.*Proto com.android.connectivity.@0
+
 # Remaining are connectivity sources in com.android.server and com.android.server.connectivity:
 # TODO: move to a subpackage of com.android.connectivity (such as com.android.connectivity.server)
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index ee512ec..4517b5c 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -18,6 +18,7 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/if_tun.h>
 #include <linux/ioctl.h>
 #include <log/log.h>
@@ -27,7 +28,11 @@
 #include <sys/wait.h>
 #include <string>
 
+#include <bpf/BpfMap.h>
+#include <bpf/BpfUtils.h>
+#include <bpf_shared.h>
 #include <netjniutils/netjniutils.h>
+#include <private/android_filesystem_config.h>
 
 #include "libclat/bpfhelper.h"
 #include "libclat/clatutils.h"
@@ -318,30 +323,13 @@
         return -1;
     }
 
-    // 1. create a throwaway socket to reserve a file descriptor number
-    int passedTunFd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-    if (passedTunFd == -1) {
-        throwIOException(env, "socket(ipv6/udp) for tun fd failed", errno);
-        return -1;
-    }
-    int passedSockRead = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-    if (passedSockRead == -1) {
-        throwIOException(env, "socket(ipv6/udp) for read socket failed", errno);
-        return -1;
-    }
-    int passedSockWrite = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-    if (passedSockWrite == -1) {
-        throwIOException(env, "socket(ipv6/udp) for write socket failed", errno);
-        return -1;
-    }
-
-    // these are the FD we'll pass to clatd on the cli, so need it as a string
-    char passedTunFdStr[INT32_STRLEN];
-    char passedSockReadStr[INT32_STRLEN];
-    char passedSockWriteStr[INT32_STRLEN];
-    snprintf(passedTunFdStr, sizeof(passedTunFdStr), "%d", passedTunFd);
-    snprintf(passedSockReadStr, sizeof(passedSockReadStr), "%d", passedSockRead);
-    snprintf(passedSockWriteStr, sizeof(passedSockWriteStr), "%d", passedSockWrite);
+    // 1. these are the FD we'll pass to clatd on the cli, so need it as a string
+    char tunFdStr[INT32_STRLEN];
+    char sockReadStr[INT32_STRLEN];
+    char sockWriteStr[INT32_STRLEN];
+    snprintf(tunFdStr, sizeof(tunFdStr), "%d", tunFd);
+    snprintf(sockReadStr, sizeof(sockReadStr), "%d", readSock);
+    snprintf(sockWriteStr, sizeof(sockWriteStr), "%d", writeSock);
 
     // 2. we're going to use this as argv[0] to clatd to make ps output more useful
     std::string progname("clatd-");
@@ -353,9 +341,9 @@
                           "-p", pfx96Str.c_str(),
                           "-4", v4Str.c_str(),
                           "-6", v6Str.c_str(),
-                          "-t", passedTunFdStr,
-                          "-r", passedSockReadStr,
-                          "-w", passedSockWriteStr,
+                          "-t", tunFdStr,
+                          "-r", sockReadStr,
+                          "-w", sockWriteStr,
                           nullptr};
     // clang-format on
 
@@ -375,7 +363,9 @@
 
     // 4. register dup2() action: this is what 'clears' the CLOEXEC flag
     // on the tun fd that we want the child clatd process to inherit
-    // (this will happen after the vfork, and before the execve)
+    // (this will happen after the vfork, and before the execve).
+    // Note that even though dup2(2) is a no-op if fd == new_fd but O_CLOEXEC flag will be removed.
+    // See implementation of bionic's posix_spawn_file_actions_adddup2().
     posix_spawn_file_actions_t fa;
     if (int ret = posix_spawn_file_actions_init(&fa)) {
         posix_spawnattr_destroy(&attr);
@@ -383,19 +373,19 @@
         return -1;
     }
 
-    if (int ret = posix_spawn_file_actions_adddup2(&fa, tunFd, passedTunFd)) {
+    if (int ret = posix_spawn_file_actions_adddup2(&fa, tunFd, tunFd)) {
         posix_spawnattr_destroy(&attr);
         posix_spawn_file_actions_destroy(&fa);
         throwIOException(env, "posix_spawn_file_actions_adddup2 for tun fd failed", ret);
         return -1;
     }
-    if (int ret = posix_spawn_file_actions_adddup2(&fa, readSock, passedSockRead)) {
+    if (int ret = posix_spawn_file_actions_adddup2(&fa, readSock, readSock)) {
         posix_spawnattr_destroy(&attr);
         posix_spawn_file_actions_destroy(&fa);
         throwIOException(env, "posix_spawn_file_actions_adddup2 for read socket failed", ret);
         return -1;
     }
-    if (int ret = posix_spawn_file_actions_adddup2(&fa, writeSock, passedSockWrite)) {
+    if (int ret = posix_spawn_file_actions_adddup2(&fa, writeSock, writeSock)) {
         posix_spawnattr_destroy(&attr);
         posix_spawn_file_actions_destroy(&fa);
         throwIOException(env, "posix_spawn_file_actions_adddup2 for write socket failed", ret);
@@ -414,7 +404,7 @@
     posix_spawnattr_destroy(&attr);
     posix_spawn_file_actions_destroy(&fa);
 
-    // 5. Start BPF if any
+    // 6. Start BPF if any
     if (!net::clat::initMaps()) {
         net::clat::ClatdTracker tracker = {};
         if (!initTracker(ifaceStr.c_str(), pfx96Str.c_str(), v4Str.c_str(), v6Str.c_str(),
@@ -488,6 +478,72 @@
     stopClatdProcess(pid);
 }
 
+static jlong com_android_server_connectivity_ClatCoordinator_tagSocketAsClat(
+        JNIEnv* env, jobject clazz, jobject sockJavaFd) {
+    int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
+    if (sockFd < 0) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket file descriptor");
+        return -1;
+    }
+
+    uint64_t sock_cookie = bpf::getSocketCookie(sockFd);
+    if (sock_cookie == bpf::NONEXISTENT_COOKIE) {
+        throwIOException(env, "get socket cookie failed", errno);
+        return -1;
+    }
+
+    bpf::BpfMap<uint64_t, UidTagValue> cookieTagMap;
+    auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH);
+    if (!res.ok()) {
+        throwIOException(env, "failed to init the cookieTagMap", res.error().code());
+        return -1;
+    }
+
+    // Tag raw socket with uid AID_CLAT and set tag as zero because tag is unused in bpf
+    // program for counting data usage in netd.c. Tagging socket is used to avoid counting
+    // duplicated clat traffic in bpf stat.
+    UidTagValue newKey = {.uid = (uint32_t)AID_CLAT, .tag = 0 /* unused */};
+    res = cookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
+    if (!res.ok()) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Failed to tag the socket: %s, fd: %d",
+                             strerror(res.error().code()), cookieTagMap.getMap().get());
+        return -1;
+    }
+
+    ALOGI("tag uid AID_CLAT to socket fd %d, cookie %" PRIu64 "", sockFd, sock_cookie);
+    return static_cast<jlong>(sock_cookie);
+}
+
+static void com_android_server_connectivity_ClatCoordinator_untagSocket(JNIEnv* env, jobject clazz,
+                                                                        jlong cookie) {
+    uint64_t sock_cookie = static_cast<uint64_t>(cookie);
+    if (sock_cookie == bpf::NONEXISTENT_COOKIE) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket cookie");
+        return;
+    }
+
+    // The reason that deleting entry from cookie tag map directly is that the tag socket destroy
+    // listener only monitors on group INET_TCP, INET_UDP, INET6_TCP, INET6_UDP. The other socket
+    // types, ex: raw, are not able to be removed automatically by the listener.
+    // See TrafficController::makeSkDestroyListener.
+    bpf::BpfMap<uint64_t, UidTagValue> cookieTagMap;
+    auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH);
+    if (!res.ok()) {
+        throwIOException(env, "failed to init the cookieTagMap", res.error().code());
+        return;
+    }
+
+    res = cookieTagMap.deleteValue(sock_cookie);
+    if (!res.ok()) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Failed to untag the socket: %s",
+                             strerror(res.error().code()));
+        return;
+    }
+
+    ALOGI("untag socket cookie %" PRIu64 "", sock_cookie);
+    return;
+}
+
 /*
  * JNI registration.
  */
@@ -517,6 +573,10 @@
         {"native_stopClatd",
          "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
          (void*)com_android_server_connectivity_ClatCoordinator_stopClatd},
+        {"native_tagSocketAsClat", "(Ljava/io/FileDescriptor;)J",
+         (void*)com_android_server_connectivity_ClatCoordinator_tagSocketAsClat},
+        {"native_untagSocket", "(J)V",
+         (void*)com_android_server_connectivity_ClatCoordinator_untagSocket},
 };
 
 int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env) {
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index f2ca18b..c006bc6 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -54,6 +54,13 @@
         sInitialized = true;
     }
 
+    /** Constructor used after T that doesn't need to use netd anymore. */
+    public BpfNetMaps() {
+        this(null);
+
+        if (USE_NETD) throw new IllegalArgumentException("BpfNetMaps need to use netd before T");
+    }
+
     public BpfNetMaps(INetd netd) {
         ensureInitialized();
         mNetd = netd;
@@ -69,15 +76,10 @@
      * Add naughty app bandwidth rule for specific app
      *
      * @param uid uid of target app
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void addNaughtyApp(final int uid) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.bandwidthAddNaughtyApp(uid);
-            return;
-        }
+    public void addNaughtyApp(final int uid) {
         final int err = native_addNaughtyApp(uid);
         maybeThrow(err, "Unable to add naughty app");
     }
@@ -86,15 +88,10 @@
      * Remove naughty app bandwidth rule for specific app
      *
      * @param uid uid of target app
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void removeNaughtyApp(final int uid) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.bandwidthRemoveNaughtyApp(uid);
-            return;
-        }
+    public void removeNaughtyApp(final int uid) {
         final int err = native_removeNaughtyApp(uid);
         maybeThrow(err, "Unable to remove naughty app");
     }
@@ -103,15 +100,10 @@
      * Add nice app bandwidth rule for specific app
      *
      * @param uid uid of target app
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void addNiceApp(final int uid) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.bandwidthAddNiceApp(uid);
-            return;
-        }
+    public void addNiceApp(final int uid) {
         final int err = native_addNiceApp(uid);
         maybeThrow(err, "Unable to add nice app");
     }
@@ -120,15 +112,10 @@
      * Remove nice app bandwidth rule for specific app
      *
      * @param uid uid of target app
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void removeNiceApp(final int uid) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.bandwidthRemoveNiceApp(uid);
-            return;
-        }
+    public void removeNiceApp(final int uid) {
         final int err = native_removeNiceApp(uid);
         maybeThrow(err, "Unable to remove nice app");
     }
@@ -138,15 +125,10 @@
      *
      * @param childChain target chain to enable
      * @param enable     whether to enable or disable child chain.
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void setChildChain(final int childChain, final boolean enable) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.firewallEnableChildChain(childChain, enable);
-            return;
-        }
+    public void setChildChain(final int childChain, final boolean enable) {
         final int err = native_setChildChain(childChain, enable);
         maybeThrow(err, "Unable to set child chain");
     }
@@ -163,14 +145,9 @@
      * @param isAllowlist Whether this is an allowlist or denylist chain.
      * @param uids        The list of UIDs to allow/deny.
      * @return 0 if the chain was successfully replaced, errno otherwise.
-     * @throws RemoteException when netd has crashed.
      */
     public int replaceUidChain(final String chainName, final boolean isAllowlist,
-            final int[] uids) throws RemoteException {
-        if (USE_NETD) {
-            mNetd.firewallReplaceUidChain(chainName, isAllowlist, uids);
-            return 0;
-        }
+            final int[] uids) {
         final int err = native_replaceUidChain(chainName, isAllowlist, uids);
         if (err != 0) {
             Log.e(TAG, "replaceUidChain failed: " + Os.strerror(-err));
@@ -184,16 +161,10 @@
      * @param childChain   target chain
      * @param uid          uid to allow/deny
      * @param firewallRule either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void setUidRule(final int childChain, final int uid, final int firewallRule)
-            throws RemoteException {
-        if (USE_NETD) {
-            mNetd.firewallSetUidRule(childChain, uid, firewallRule);
-            return;
-        }
+    public void setUidRule(final int childChain, final int uid, final int firewallRule) {
         final int err = native_setUidRule(childChain, uid, firewallRule);
         maybeThrow(err, "Unable to set uid rule");
     }
@@ -247,15 +218,10 @@
     /**
      * Request netd to change the current active network stats map.
      *
-     * @throws RemoteException when netd has crashed.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    public void swapActiveStatsMap() throws RemoteException {
-        if (USE_NETD) {
-            mNetd.trafficSwapActiveStatsMap();
-            return;
-        }
+    public void swapActiveStatsMap() {
         final int err = native_swapActiveStatsMap();
         maybeThrow(err, "Unable to swap active stats map");
     }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index eabcd10..da80e0a 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -198,7 +198,6 @@
 import android.net.resolv.aidl.Nat64PrefixEventParcel;
 import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
 import android.net.shared.PrivateDnsConfig;
-import android.net.util.InterfaceParams;
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.BatteryStatsManager;
 import android.os.Binder;
@@ -245,6 +244,7 @@
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.net.module.util.LocationPermissionChecker;
@@ -936,7 +936,7 @@
             }
             // Ethernet is often not specified in the configs, although many devices can use it via
             // USB host adapters. Add it as long as the ethernet service is here.
-            if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) {
+            if (deviceSupportsEthernet(ctx)) {
                 addSupportedType(TYPE_ETHERNET);
             }
 
@@ -1621,6 +1621,15 @@
                 mContext);
     }
 
+    /**
+     * Check whether or not the device supports Ethernet transport.
+     */
+    public static boolean deviceSupportsEthernet(final Context context) {
+        final PackageManager pm = context.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET)
+                || pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST);
+    }
+
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         return createDefaultNetworkCapabilitiesForUidRangeSet(Collections.singleton(
                 new UidRange(uid, uid)));
@@ -10699,6 +10708,9 @@
     }
 
     private boolean canNetworkBeRateLimited(@NonNull final NetworkAgentInfo networkAgent) {
+        // Rate-limiting cannot run correctly before T because the BPF program is not loaded.
+        if (!SdkLevel.isAtLeastT()) return false;
+
         final NetworkCapabilities agentCaps = networkAgent.networkCapabilities;
         // Only test networks (they cannot hold NET_CAPABILITY_INTERNET) and networks that provide
         // internet connectivity can be rate limited.
@@ -11056,7 +11068,7 @@
             } else {
                 mBpfNetMaps.removeNiceApp(uid);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11071,7 +11083,7 @@
             } else {
                 mBpfNetMaps.removeNaughtyApp(uid);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11083,7 +11095,7 @@
         try {
             mBpfNetMaps.setUidRule(chain, uid,
                     allow ? INetd.FIREWALL_RULE_ALLOW : INetd.FIREWALL_RULE_DENY);
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11094,7 +11106,7 @@
 
         try {
             mBpfNetMaps.setChildChain(chain, enable);
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11125,17 +11137,7 @@
                     throw new IllegalArgumentException("replaceFirewallChain with invalid chain: "
                             + chain);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void swapActiveStatsMap() {
-        enforceNetworkStackOrSettingsPermission();
-        try {
-            mBpfNetMaps.swapActiveStatsMap();
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index c57983b..c1a8195 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -67,6 +67,7 @@
 
     private static final int INVALID_IFINDEX = 0;
     private static final int INVALID_PID = 0;
+    private static final long INVALID_COOKIE = 0;
 
     @NonNull
     private final INetd mNetd;
@@ -81,6 +82,7 @@
     @Nullable
     private String mXlatLocalAddress6 = null;
     private int mPid = INVALID_PID;
+    private long mCookie = INVALID_COOKIE;
 
     @VisibleForTesting
     abstract static class Dependencies {
@@ -185,6 +187,20 @@
                 throws IOException {
             native_stopClatd(iface, pfx96, v4, v6, pid);
         }
+
+        /**
+         * Tag socket as clat.
+         */
+        public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException {
+            return native_tagSocketAsClat(sock);
+        }
+
+        /**
+         * Untag socket.
+         */
+        public void untagSocket(long cookie) throws IOException {
+            native_untagSocket(cookie);
+        }
     }
 
     @VisibleForTesting
@@ -335,6 +351,17 @@
             throw new IOException("add anycast sockopt failed: " + e);
         }
 
+        // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
+        long cookie;
+        try {
+            cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor());
+        } catch (IOException e) {
+            tunFd.close();
+            readSock6.close();
+            writeSock6.close();
+            throw new IOException("tag raw socket failed: " + e);
+        }
+
         // Update our packet socket filter to reflect the new 464xlat IP address.
         try {
             mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex);
@@ -353,7 +380,9 @@
             mNat64Prefix = pfx96;
             mXlatLocalAddress4 = v4;
             mXlatLocalAddress6 = v6;
+            mCookie = cookie;
         } catch (IOException e) {
+            mDeps.untagSocket(cookie);
             throw new IOException("Error start clatd on " + iface + ": " + e);
         } finally {
             tunFd.close();
@@ -374,7 +403,7 @@
         Log.i(TAG, "Stopping clatd pid=" + mPid + " on " + mIface);
 
         mDeps.stopClatd(mIface, mNat64Prefix, mXlatLocalAddress4, mXlatLocalAddress6, mPid);
-        // TODO: remove setIptablesDropRule
+        mDeps.untagSocket(mCookie);
 
         Log.i(TAG, "clatd on " + mIface + " stopped");
 
@@ -383,6 +412,7 @@
         mXlatLocalAddress4 = null;
         mXlatLocalAddress6 = null;
         mPid = INVALID_PID;
+        mCookie = INVALID_COOKIE;
     }
 
     private static native String native_selectIpv4Address(String v4addr, int prefixlen)
@@ -403,4 +433,6 @@
             throws IOException;
     private static native void native_stopClatd(String iface, String pfx96, String v4, String v6,
             int pid) throws IOException;
+    private static native long native_tagSocketAsClat(FileDescriptor sock) throws IOException;
+    private static native void native_untagSocket(long cookie) throws IOException;
 }
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 f82e377..9894444 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
@@ -899,6 +899,11 @@
         }
     }
 
+    protected void startActivity() throws Exception {
+        final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_ACTIVTIY);
+        mContext.startActivity(launchIntent);
+    }
+
     private void startForegroundService() throws Exception {
         final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE);
         mContext.startForegroundService(launchIntent);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index 604a0b6..2f30536 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 
+import static com.android.compatibility.common.util.FeatureUtil.isTV;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
 import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
 import static com.android.cts.net.hostside.Property.METERED_NETWORK;
@@ -27,14 +28,14 @@
 
 import static org.junit.Assert.fail;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.compatibility.common.util.CddTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import androidx.test.filters.LargeTest;
-
 @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
 @LargeTest
 public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
@@ -113,6 +114,11 @@
         turnScreenOff();
         assertBackgroundNetworkAccess(false);
         turnScreenOn();
+        // On some TVs, it is possible that the activity on top may change after the screen is
+        // turned off and on again, so relaunch the activity in the test app again.
+        if (isTV()) {
+            startActivity();
+        }
         assertForegroundNetworkAccess();
 
         // Goes back to background state.
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
index 0526a75..78ae7b8 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -62,6 +62,7 @@
                     "dumpsys network_management",
                     "dumpsys usagestats " + TEST_PKG + " " + TEST_APP2_PKG,
                     "dumpsys usagestats appstandby",
+                    "dumpsys connectivity trafficcontroller",
                     "dumpsys netd trafficcontroller",
             }) {
                 dumpCommandOutput(out, cmd);
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index d605799..48a1c79 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -30,6 +30,8 @@
     </target_preparer>
     <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
     </target_preparer>
+    <target_preparer class="com.android.testutils.DisableConfigSyncTargetPreparer">
+    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.net.cts" />
         <option name="runtime-hint" value="9m4s" />
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 1967e53..988f21b 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -59,6 +59,7 @@
     name: "non-connectivity-module-test",
     srcs: [
         "java/android/app/usage/*.java",
+        "java/android/net/EthernetNetworkUpdateRequestTest.java",
         "java/android/net/Ikev2VpnProfileTest.java",
         "java/android/net/IpMemoryStoreTest.java",
         "java/android/net/IpSecAlgorithmTest.java",
diff --git a/tests/unit/java/android/net/EthernetNetworkUpdateRequestTest.java b/tests/unit/java/android/net/EthernetNetworkUpdateRequestTest.java
new file mode 100644
index 0000000..314fbcf
--- /dev/null
+++ b/tests/unit/java/android/net/EthernetNetworkUpdateRequestTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.net;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+public class EthernetNetworkUpdateRequestTest {
+    private IpConfiguration buildIpConfiguration() {
+        return new IpConfiguration.Builder().setHttpProxy(
+                new ProxyInfo("test.example.com", 1234, "")).build();
+    }
+
+    private NetworkCapabilities buildNetworkCapabilities() {
+        return new NetworkCapabilities.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_ETHERNET).build();
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        EthernetNetworkUpdateRequest reqWithNonNull =
+                new EthernetNetworkUpdateRequest.Builder().setIpConfiguration(
+                        buildIpConfiguration()).setNetworkCapabilities(
+                        buildNetworkCapabilities()).build();
+        EthernetNetworkUpdateRequest reqWithNullCaps =
+                new EthernetNetworkUpdateRequest.Builder().setIpConfiguration(
+                        buildIpConfiguration()).build();
+
+        assertParcelSane(reqWithNonNull, 2);
+        assertParcelSane(reqWithNullCaps, 2);
+    }
+}
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index ac21e77..f07a10d 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server;
 
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
-import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.PERMISSION_INTERNET;
 
 import static org.junit.Assume.assumeFalse;
@@ -60,26 +58,10 @@
     @Test
     public void testBpfNetMapsBeforeT() throws Exception {
         assumeFalse(SdkLevel.isAtLeastT());
-        mBpfNetMaps.addNaughtyApp(TEST_UID);
-        verify(mNetd).bandwidthAddNaughtyApp(TEST_UID);
-        mBpfNetMaps.removeNaughtyApp(TEST_UID);
-        verify(mNetd).bandwidthRemoveNaughtyApp(TEST_UID);
-        mBpfNetMaps.addNiceApp(TEST_UID);
-        verify(mNetd).bandwidthAddNiceApp(TEST_UID);
-        mBpfNetMaps.removeNiceApp(TEST_UID);
-        verify(mNetd).bandwidthRemoveNiceApp(TEST_UID);
-        mBpfNetMaps.setChildChain(FIREWALL_CHAIN_DOZABLE, true);
-        verify(mNetd).firewallEnableChildChain(FIREWALL_CHAIN_DOZABLE, true);
-        mBpfNetMaps.replaceUidChain(CHAINNAME, true, TEST_UIDS);
-        verify(mNetd).firewallReplaceUidChain(CHAINNAME, true, TEST_UIDS);
-        mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, FIREWALL_RULE_ALLOW);
-        verify(mNetd).firewallSetUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, FIREWALL_RULE_ALLOW);
         mBpfNetMaps.addUidInterfaceRules(IFNAME, TEST_UIDS);
         verify(mNetd).firewallAddUidInterfaceRules(IFNAME, TEST_UIDS);
         mBpfNetMaps.removeUidInterfaceRules(TEST_UIDS);
         verify(mNetd).firewallRemoveUidInterfaceRules(TEST_UIDS);
-        mBpfNetMaps.swapActiveStatsMap();
-        verify(mNetd).trafficSwapActiveStatsMap();
         mBpfNetMaps.setNetPermForUids(PERMISSION_INTERNET, TEST_UIDS);
         verify(mNetd).trafficSetNetPermForUids(PERMISSION_INTERNET, TEST_UIDS);
     }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 777da17..94272b8 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -36,6 +36,7 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FEATURE_ETHERNET;
 import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -137,6 +138,9 @@
 import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
 import static com.android.testutils.ConcurrentUtils.await;
 import static com.android.testutils.ConcurrentUtils.durationOf;
+import static com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 import static com.android.testutils.ExceptionUtils.ignoreExceptions;
 import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
 import static com.android.testutils.MiscAsserts.assertContainsAll;
@@ -358,6 +362,7 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.AdditionalAnswers;
@@ -420,6 +425,9 @@
 public class ConnectivityServiceTest {
     private static final String TAG = "ConnectivityServiceTest";
 
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
     private static final int TIMEOUT_MS = 2_000;
     // Broadcasts can take a long time to be delivered. The test will not wait for that long unless
     // there is a failure, so use a long timeout.
@@ -1732,6 +1740,7 @@
         mockDefaultPackages();
         mockHasSystemFeature(FEATURE_WIFI, true);
         mockHasSystemFeature(FEATURE_WIFI_DIRECT, true);
+        mockHasSystemFeature(FEATURE_ETHERNET, true);
         doReturn(true).when(mTelephonyManager).isDataCapable();
 
         FakeSettingsProvider.clearSettingsProvider();
@@ -15397,7 +15406,7 @@
                 null /* callingAttributionTag */));
     }
 
-    @Test
+    @Test @IgnoreUpTo(SC_V2)
     public void testUpdateRateLimit_EnableDisable() throws Exception {
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
@@ -15436,7 +15445,7 @@
                 it -> it.first == cellLp.getInterfaceName() && it.second == -1));
     }
 
-    @Test
+    @Test @IgnoreUpTo(SC_V2)
     public void testUpdateRateLimit_WhenNewNetworkIsAdded() throws Exception {
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
@@ -15462,7 +15471,7 @@
                 && it.second == rateLimitInBytesPerSec));
     }
 
-    @Test
+    @Test @IgnoreUpTo(SC_V2)
     public void testUpdateRateLimit_OnlyAffectsInternetCapableNetworks() throws Exception {
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
@@ -15480,7 +15489,7 @@
         assertNull(readHeadWifi.poll(TIMEOUT_MS, it -> it.first == wifiLp.getInterfaceName()));
     }
 
-    @Test
+    @Test @IgnoreUpTo(SC_V2)
     public void testUpdateRateLimit_DisconnectingResetsRateLimit()
             throws Exception {
         // Steps:
@@ -15516,7 +15525,7 @@
         assertNull(readHeadWifi.poll(TIMEOUT_MS, it -> it.first == wifiLp.getInterfaceName()));
     }
 
-    @Test
+    @Test @IgnoreUpTo(SC_V2)
     public void testUpdateRateLimit_UpdateExistingRateLimit() throws Exception {
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
@@ -15545,4 +15554,21 @@
                 it -> it.first == wifiLp.getInterfaceName()
                         && it.second == 2000));
     }
+
+    @Test @IgnoreAfter(SC_V2)
+    public void testUpdateRateLimit_DoesNothingBeforeT() throws Exception {
+        final LinkProperties wifiLp = new LinkProperties();
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+
+        final ArrayTrackRecord<Pair<String, Long>>.ReadHead readHead =
+                mDeps.mRateLimitHistory.newReadHead();
+
+        setIngressRateLimit(1000);
+        waitForIdle();
+
+        assertNull(readHead.poll(TEST_CALLBACK_TIMEOUT_MS, it -> true));
+    }
 }
diff --git a/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
index 64736f2..7ed55e5 100644
--- a/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -23,6 +23,8 @@
 
 import android.content.Context
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_ETHERNET
+import android.content.pm.PackageManager.FEATURE_USB_HOST
 import android.content.pm.PackageManager.FEATURE_WIFI
 import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
 import android.net.ConnectivityManager.TYPE_ETHERNET
@@ -40,7 +42,6 @@
 import android.net.ConnectivityManager.TYPE_WIFI
 import android.net.ConnectivityManager.TYPE_WIFI_P2P
 import android.net.ConnectivityManager.TYPE_WIMAX
-import android.net.EthernetManager
 import android.net.NetworkInfo.DetailedState.CONNECTED
 import android.net.NetworkInfo.DetailedState.DISCONNECTED
 import android.os.Build
@@ -82,9 +83,8 @@
     private val mContext = mock(Context::class.java).apply {
         doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI)
         doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+        doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_ETHERNET)
         doReturn(mPm).`when`(this).packageManager
-        doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService(
-                Context.ETHERNET_SERVICE)
     }
     private val mTm = mock(TelephonyManager::class.java).apply {
         doReturn(true).`when`(this).isDataCapable
@@ -105,7 +105,8 @@
 
     @Test
     fun testSupportedTypes_NoEthernet() {
-        doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE)
+        doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_ETHERNET)
+        doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_USB_HOST)
         assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET))
     }
 
diff --git a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
index a3b0e7c..7688a6b 100644
--- a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
@@ -250,7 +250,7 @@
         mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, true);
         assertTrue("Should be true since mobile data usage is restricted",
                 mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).updateMeteredNetworkDenyList(TEST_UID, true /* enabled */);
+        verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
 
         mNMService.setDataSaverModeEnabled(true);
         verify(mNetdService).bandwidthEnableDataSaver(true);
@@ -258,16 +258,16 @@
         mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false);
         assertTrue("Should be true since data saver is on and the uid is not allowlisted",
                 mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).updateMeteredNetworkDenyList(TEST_UID, true /* false */);
+        verify(mCm).removeUidFromMeteredNetworkDenyList(TEST_UID);
 
         mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, true);
         assertFalse("Should be false since data saver is on and the uid is allowlisted",
                 mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).updateMeteredNetworkAllowList(TEST_UID, true /* enabled */);
+        verify(mCm).addUidToMeteredNetworkAllowList(TEST_UID);
 
         // remove uid from allowlist and turn datasaver off again
         mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
-        verify(mCm).updateMeteredNetworkAllowList(TEST_UID, false /* enabled */);
+        verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
         mNMService.setDataSaverModeEnabled(false);
         verify(mNetdService).bandwidthEnableDataSaver(false);
         assertFalse("Network should not be restricted when data saver is off",
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 84e02ce..8a2cfc2 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -26,13 +26,10 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
 
 import android.annotation.NonNull;
 import android.net.INetd;
@@ -82,6 +79,7 @@
     private static final int TUN_FD = 534;
     private static final int RAW_SOCK_FD = 535;
     private static final int PACKET_SOCK_FD = 536;
+    private static final long RAW_SOCK_COOKIE = 27149;
     private static final ParcelFileDescriptor TUN_PFD = new ParcelFileDescriptor(
             new FileDescriptor());
     private static final ParcelFileDescriptor RAW_SOCK_PFD = new ParcelFileDescriptor(
@@ -258,12 +256,35 @@
         /**
          * Stop clatd.
          */
+        @Override
         public void stopClatd(@NonNull String iface, @NonNull String pfx96, @NonNull String v4,
                 @NonNull String v6, int pid) throws IOException {
             if (pid == -1) {
                 fail("unsupported arg: " + pid);
             }
         }
+
+        /**
+         * Tag socket as clat.
+         */
+        @Override
+        public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException {
+            if (Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), sock)) {
+                return RAW_SOCK_COOKIE;
+            }
+            fail("unsupported arg: " + sock);
+            return 0;
+        }
+
+        /**
+         * Untag socket.
+         */
+        @Override
+        public void untagSocket(long cookie) throws IOException {
+            if (cookie != RAW_SOCK_COOKIE) {
+                fail("unsupported arg: " + cookie);
+            }
+        }
     };
 
     @NonNull
@@ -326,6 +347,8 @@
         inOrder.verify(mDeps).addAnycastSetsockopt(
                 argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)),
                 eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX));
+        inOrder.verify(mDeps).tagSocketAsClat(
+                argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)));
         inOrder.verify(mDeps).configurePacketSocket(
                 argThat(fd -> Objects.equals(PACKET_SOCK_PFD.getFileDescriptor(), fd)),
                 eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX));
@@ -348,13 +371,12 @@
         coordinator.clatStop();
         inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
                 eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
+        inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
         inOrder.verifyNoMoreInteractions();
 
         // [4] Expect an IO exception while stopping a clatd that doesn't exist.
         assertThrows("java.io.IOException: Clatd has not started", IOException.class,
                 () -> coordinator.clatStop());
-        inOrder.verify(mDeps, never()).stopClatd(anyString(), anyString(), anyString(),
-                anyString(), anyInt());
         inOrder.verifyNoMoreInteractions();
     }
 
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 6872f80..79744b1 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -32,12 +32,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
@@ -77,7 +74,6 @@
     private File mTestProc;
     private NetworkStatsFactory mFactory;
     @Mock private Context mContext;
-    @Mock private ConnectivityManager mCm;
 
     @Before
     public void setUp() throws Exception {
@@ -88,9 +84,6 @@
         // applications. So in order to have a test support native library, the native code
         // related to networkStatsFactory is compiled to a minimal native library and loaded here.
         System.loadLibrary("networkstatsfactorytestjni");
-        doReturn(Context.CONNECTIVITY_SERVICE).when(mContext).getSystemServiceName(
-                eq(ConnectivityManager.class));
-        doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE));
         mFactory = new NetworkStatsFactory(mContext, mTestProc, false);
         mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
     }