Merge "BpfHandler: only allow to tag INET/INET6 socket"
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 8459a2c..dd04d6c 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/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 07fce08..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) {
@@ -2463,7 +2475,7 @@
         final boolean tetherEnabledInSettings = tetherSupported
                 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
 
-        return tetherEnabledInSettings && hasTetherableConfiguration()
+        return tetherEnabledInSettings && hasAnySupportedDownstream()
                 && !isProvisioningNeededButUnavailable();
     }
 
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/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index ae5d389..0388758 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -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/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/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index f2ca18b..c977391 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -69,15 +69,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 +81,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 +93,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 +105,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 +118,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 +138,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 +154,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");
     }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ab78104..e0bf223 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -10699,6 +10699,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 +11059,7 @@
             } else {
                 mBpfNetMaps.removeNiceApp(uid);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11071,7 +11074,7 @@
             } else {
                 mBpfNetMaps.removeNaughtyApp(uid);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11083,7 +11086,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 +11097,7 @@
 
         try {
             mBpfNetMaps.setChildChain(chain, enable);
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -11125,7 +11128,7 @@
                     throw new IllegalArgumentException("replaceFirewallChain with invalid chain: "
                             + chain);
             }
-        } catch (RemoteException | ServiceSpecificException e) {
+        } catch (ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index ac21e77..2959ac9 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,20 +58,6 @@
     @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);
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 777da17..c8dc107 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -137,6 +137,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 +361,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 +424,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.
@@ -15397,7 +15404,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 +15443,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 +15469,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 +15487,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 +15523,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 +15552,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));
+    }
 }