Merge changes from topic "bandwidth-limiting"

* changes:
  Add bandwidth limiting to CS
  Add setting that controls network rate limit
This commit is contained in:
Patrick Rohr
2022-02-09 18:00:04 +00:00
committed by Gerrit Code Review
5 changed files with 373 additions and 1 deletions

View File

@@ -69,6 +69,7 @@ package android.net {
method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int);
method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
method public static long getIngressRateLimitInBytesPerSecond(@NonNull android.content.Context);
method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
method @NonNull public static java.util.Set<java.lang.Integer> getMobileDataPreferredUids(@NonNull android.content.Context);
@@ -89,6 +90,7 @@ package android.net {
method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int);
method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
method public static void setIngressRateLimitInBytesPerSecond(@NonNull android.content.Context, @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) long);
method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
method public static void setMobileDataPreferredUids(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);

View File

@@ -383,6 +383,14 @@ public class ConnectivitySettingsManager {
public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
"uids_allowed_on_restricted_networks";
/**
* A global rate limit that applies to all networks with NET_CAPABILITY_INTERNET when enabled.
*
* @hide
*/
public static final String INGRESS_RATE_LIMIT_BYTES_PER_SECOND =
"ingress_rate_limit_bytes_per_second";
/**
* Get mobile data activity timeout from {@link Settings}.
*
@@ -1071,4 +1079,37 @@ public class ConnectivitySettingsManager {
Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
uids);
}
/**
* Get the global network bandwidth rate limit.
*
* The limit is only applicable to networks that provide internet connectivity. If the setting
* is unset, it defaults to -1.
*
* @param context The {@link Context} to query the setting.
* @return The rate limit in number of bytes per second or -1 if disabled.
*/
public static long getIngressRateLimitInBytesPerSecond(@NonNull Context context) {
return Settings.Global.getLong(context.getContentResolver(),
INGRESS_RATE_LIMIT_BYTES_PER_SECOND, -1);
}
/**
* Set the global network bandwidth rate limit.
*
* The limit is only applicable to networks that provide internet connectivity.
*
* @param context The {@link Context} to set the setting.
* @param rateLimitInBytesPerSec The rate limit in number of bytes per second or -1 to disable.
*/
public static void setIngressRateLimitInBytesPerSecond(@NonNull Context context,
@IntRange(from = -1, to = Integer.MAX_VALUE) long rateLimitInBytesPerSec) {
if (rateLimitInBytesPerSec < -1) {
throw new IllegalArgumentException(
"Rate limit must be within the range [-1, Integer.MAX_VALUE]");
}
Settings.Global.putLong(context.getContentResolver(),
INGRESS_RATE_LIMIT_BYTES_PER_SECOND,
rateLimitInBytesPerSec);
}
}

View File

@@ -90,6 +90,7 @@ import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequi
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.system.OsConstants.ETH_P_ALL;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -197,6 +198,7 @@ import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
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;
@@ -248,6 +250,7 @@ import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.TcUtils;
import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
@@ -274,6 +277,7 @@ import com.android.server.connectivity.UidRangeUtils;
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.Inet4Address;
@@ -708,6 +712,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_SET_TEST_ALLOW_BAD_WIFI_UNTIL = 55;
/**
* Used internally when INGRESS_RATE_LIMIT_BYTES_PER_SECOND setting changes.
*/
private static final int EVENT_INGRESS_RATE_LIMIT_CHANGED = 56;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
@@ -725,6 +734,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final long MAX_TEST_ALLOW_BAD_WIFI_UNTIL_MS = 5 * 60 * 1000L;
/**
* The priority of the tc police rate limiter -- smaller value is higher priority.
* This value needs to be coordinated with PRIO_CLAT, PRIO_TETHER4, and PRIO_TETHER6.
*/
private static final short TC_PRIO_POLICE = 1;
/**
* The BPF program attached to the tc-police hook to account for to-be-dropped traffic.
*/
private static final String TC_POLICE_BPF_PROG_PATH =
"/sys/fs/bpf/prog_netd_schedact_ingress_account";
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -815,6 +836,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
final Map<IBinder, ConnectivityDiagnosticsCallbackInfo> mConnectivityDiagnosticsCallbacks =
new HashMap<>();
// Rate limit applicable to all internet capable networks (-1 = disabled). This value is
// configured via {@link
// ConnectivitySettingsManager#INGRESS_RATE_LIMIT_BYTES_PER_SECOND}
// Only the handler thread is allowed to access this field.
private long mIngressRateLimit = -1;
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -1367,6 +1394,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
public BpfNetMaps getBpfNetMaps(INetd netd) {
return new BpfNetMaps(netd);
}
/**
* Wraps {@link TcUtils#tcFilterAddDevIngressPolice}
*/
public void enableIngressRateLimit(String iface, long rateInBytesPerSecond) {
final InterfaceParams params = InterfaceParams.getByName(iface);
if (params == null) {
// the interface might have disappeared.
logw("Failed to get interface params for interface " + iface);
return;
}
try {
// converting rateInBytesPerSecond from long to int is safe here because the
// setting's range is limited to INT_MAX.
// TODO: add long/uint64 support to tcFilterAddDevIngressPolice.
TcUtils.tcFilterAddDevIngressPolice(params.index, TC_PRIO_POLICE, (short) ETH_P_ALL,
(int) rateInBytesPerSecond, TC_POLICE_BPF_PROG_PATH);
} catch (IOException e) {
loge("TcUtils.tcFilterAddDevIngressPolice(ifaceIndex=" + params.index
+ ", PRIO_POLICE, ETH_P_ALL, rateInBytesPerSecond="
+ rateInBytesPerSecond + ", bpfProgPath=" + TC_POLICE_BPF_PROG_PATH
+ ") failure: ", e);
}
}
/**
* Wraps {@link TcUtils#tcFilterDelDev}
*/
public void disableIngressRateLimit(String iface) {
final InterfaceParams params = InterfaceParams.getByName(iface);
if (params == null) {
// the interface might have disappeared.
logw("Failed to get interface params for interface " + iface);
return;
}
try {
TcUtils.tcFilterDelDev(params.index, true, TC_PRIO_POLICE, (short) ETH_P_ALL);
} catch (IOException e) {
loge("TcUtils.tcFilterDelDev(ifaceIndex=" + params.index
+ ", ingress=true, PRIO_POLICE, ETH_P_ALL) failure: ", e);
}
}
}
public ConnectivityService(Context context) {
@@ -1541,6 +1610,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
} catch (ErrnoException e) {
loge("Unable to create DscpPolicyTracker");
}
mIngressRateLimit = ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
mContext);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1611,6 +1683,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.sendEmptyMessage(EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
}
@VisibleForTesting
void updateIngressRateLimit() {
mHandler.sendEmptyMessage(EVENT_INGRESS_RATE_LIMIT_CHANGED);
}
private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
final boolean enable = mContext.getResources().getBoolean(id);
handleAlwaysOnNetworkRequest(networkRequest, enable);
@@ -1672,6 +1749,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
mSettingsObserver.observe(
Settings.Secure.getUriFor(ConnectivitySettingsManager.MOBILE_DATA_PREFERRED_UIDS),
EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
// Watch for ingress rate limit changes.
mSettingsObserver.observe(
Settings.Secure.getUriFor(
ConnectivitySettingsManager.INGRESS_RATE_LIMIT_BYTES_PER_SECOND),
EVENT_INGRESS_RATE_LIMIT_CHANGED);
}
private void registerPrivateDnsSettingsCallbacks() {
@@ -4091,6 +4174,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// for an unnecessarily long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
// clean up tc police filters on interface.
if (canNetworkBeRateLimited(nai) && mIngressRateLimit >= 0) {
mDeps.disableIngressRateLimit(nai.linkProperties.getInterfaceName());
}
}
mNetIdManager.releaseNetId(nai.network.getNetId());
nai.onNetworkDestroyed();
@@ -5158,6 +5246,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
final long timeMs = ((Long) msg.obj).longValue();
mMultinetworkPolicyTracker.setTestAllowBadWifiUntil(timeMs);
break;
case EVENT_INGRESS_RATE_LIMIT_CHANGED:
handleIngressRateLimitChanged();
break;
}
}
}
@@ -8854,6 +8945,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
// A network that has just connected has zero requests and is thus a foreground network.
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
// If a rate limit has been configured and is applicable to this network (network
// provides internet connectivity), apply it.
// Note: in case of a system server crash, there is a very small chance that this
// leaves some interfaces rate limited (i.e. if the rate limit had been changed just
// before the crash and was never applied). One solution would be to delete all
// potential tc police filters every time this is called. Since this is an unlikely
// scenario in the first place (and worst case, the interface stays rate limited until
// the device is rebooted), this seems a little overkill.
if (canNetworkBeRateLimited(networkAgent) && mIngressRateLimit >= 0) {
mDeps.enableIngressRateLimit(networkAgent.linkProperties.getInterfaceName(),
mIngressRateLimit);
}
if (!createNativeNetwork(networkAgent)) return;
if (networkAgent.propagateUnderlyingCapabilities()) {
// Initialize the network's capabilities to their starting values according to the
@@ -10514,6 +10618,39 @@ public class ConnectivityService extends IConnectivityManager.Stub
rematchAllNetworksAndRequests();
}
private void handleIngressRateLimitChanged() {
final long oldIngressRateLimit = mIngressRateLimit;
mIngressRateLimit = ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
mContext);
for (final NetworkAgentInfo networkAgent : mNetworkAgentInfos) {
if (canNetworkBeRateLimited(networkAgent)) {
// If rate limit has previously been enabled, remove the old limit first.
if (oldIngressRateLimit >= 0) {
mDeps.disableIngressRateLimit(networkAgent.linkProperties.getInterfaceName());
}
if (mIngressRateLimit >= 0) {
mDeps.enableIngressRateLimit(networkAgent.linkProperties.getInterfaceName(),
mIngressRateLimit);
}
}
}
}
private boolean canNetworkBeRateLimited(@NonNull final NetworkAgentInfo networkAgent) {
if (!networkAgent.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) {
// rate limits only apply to networks that provide internet connectivity.
return false;
}
final String iface = networkAgent.linkProperties.getInterfaceName();
if (iface == null) {
// This can never happen.
logwtf("canNetworkBeRateLimited: LinkProperties#getInterfaceName returns null");
return false;
}
return true;
}
private void enforceAutomotiveDevice() {
PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE,
"setOemNetworkPreference() is only available on automotive devices.");

View File

@@ -39,6 +39,7 @@ import android.net.ConnectivitySettingsManager.getConnectivityKeepPendingIntentD
import android.net.ConnectivitySettingsManager.getDnsResolverSampleRanges
import android.net.ConnectivitySettingsManager.getDnsResolverSampleValidityDuration
import android.net.ConnectivitySettingsManager.getDnsResolverSuccessThresholdPercent
import android.net.ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond
import android.net.ConnectivitySettingsManager.getMobileDataActivityTimeout
import android.net.ConnectivitySettingsManager.getMobileDataAlwaysOn
import android.net.ConnectivitySettingsManager.getNetworkSwitchNotificationMaximumDailyCount
@@ -51,6 +52,7 @@ import android.net.ConnectivitySettingsManager.setConnectivityKeepPendingIntentD
import android.net.ConnectivitySettingsManager.setDnsResolverSampleRanges
import android.net.ConnectivitySettingsManager.setDnsResolverSampleValidityDuration
import android.net.ConnectivitySettingsManager.setDnsResolverSuccessThresholdPercent
import android.net.ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond
import android.net.ConnectivitySettingsManager.setMobileDataActivityTimeout
import android.net.ConnectivitySettingsManager.setMobileDataAlwaysOn
import android.net.ConnectivitySettingsManager.setNetworkSwitchNotificationMaximumDailyCount
@@ -292,4 +294,19 @@ class ConnectivitySettingsManagerTest {
setter = { setWifiAlwaysRequested(context, it) },
testIntValues = intArrayOf(0))
}
@Test
fun testInternetNetworkRateLimitInBytesPerSecond() {
val defaultRate = getIngressRateLimitInBytesPerSecond(context)
val testRate = 1000L
setIngressRateLimitInBytesPerSecond(context, testRate)
assertEquals(testRate, getIngressRateLimitInBytesPerSecond(context))
setIngressRateLimitInBytesPerSecond(context, defaultRate)
assertEquals(defaultRate, getIngressRateLimitInBytesPerSecond(context))
assertFailsWith<IllegalArgumentException>("Expected failure, but setting accepted") {
setIngressRateLimitInBytesPerSecond(context, -10)
}
}
}

View File

@@ -179,7 +179,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static java.util.Arrays.asList;
@@ -388,6 +387,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -1963,6 +1963,25 @@ public class ConnectivityServiceTest {
public BpfNetMaps getBpfNetMaps(INetd netd) {
return mBpfNetMaps;
}
final ArrayTrackRecord<Pair<String, Long>> mRateLimitHistory = new ArrayTrackRecord<>();
final Map<String, Long> mActiveRateLimit = new HashMap<>();
@Override
public void enableIngressRateLimit(final String iface, final long rateInBytesPerSecond) {
mRateLimitHistory.add(new Pair<>(iface, rateInBytesPerSecond));
// Due to a TC limitation, the rate limit needs to be removed before it can be
// updated. Check that this happened.
assertEquals(-1L, (long) mActiveRateLimit.getOrDefault(iface, -1L));
mActiveRateLimit.put(iface, rateInBytesPerSecond);
}
@Override
public void disableIngressRateLimit(final String iface) {
mRateLimitHistory.add(new Pair<>(iface, -1L));
assertNotEquals(-1L, (long) mActiveRateLimit.getOrDefault(iface, -1L));
mActiveRateLimit.put(iface, -1L);
}
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -5027,6 +5046,13 @@ public class ConnectivityServiceTest {
waitForIdle();
}
private void setIngressRateLimit(int rateLimitInBytesPerSec) {
ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mServiceContext,
rateLimitInBytesPerSec);
mService.updateIngressRateLimit();
waitForIdle();
}
private boolean isForegroundNetwork(TestNetworkAgentWrapper network) {
NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
assertNotNull(nc);
@@ -15339,4 +15365,153 @@ public class ConnectivityServiceTest {
ConnectivityManager.TYPE_NONE, null /* hostAddress */, "com.not.package.owner",
null /* callingAttributionTag */));
}
@Test
public void testUpdateRateLimit_EnableDisable() throws Exception {
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false);
waitForIdle();
final ArrayTrackRecord<Pair<String, Long>>.ReadHead readHeadWifi =
mDeps.mRateLimitHistory.newReadHead();
final ArrayTrackRecord<Pair<String, Long>>.ReadHead readHeadCell =
mDeps.mRateLimitHistory.newReadHead();
// set rate limit to 8MBit/s => 1MB/s
final int rateLimitInBytesPerSec = 1 * 1000 * 1000;
setIngressRateLimit(rateLimitInBytesPerSec);
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName()
&& it.second == rateLimitInBytesPerSec));
assertNotNull(readHeadCell.poll(TIMEOUT_MS,
it -> it.first == cellLp.getInterfaceName()
&& it.second == rateLimitInBytesPerSec));
// disable rate limiting
setIngressRateLimit(-1);
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName() && it.second == -1));
assertNotNull(readHeadCell.poll(TIMEOUT_MS,
it -> it.first == cellLp.getInterfaceName() && it.second == -1));
}
@Test
public void testUpdateRateLimit_WhenNewNetworkIsAdded() 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();
// set rate limit to 8MBit/s => 1MB/s
final int rateLimitInBytesPerSec = 1 * 1000 * 1000;
setIngressRateLimit(rateLimitInBytesPerSec);
assertNotNull(readHead.poll(TIMEOUT_MS, it -> it.first == wifiLp.getInterfaceName()
&& it.second == rateLimitInBytesPerSec));
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false);
assertNotNull(readHead.poll(TIMEOUT_MS, it -> it.first == cellLp.getInterfaceName()
&& it.second == rateLimitInBytesPerSec));
}
@Test
public void testUpdateRateLimit_OnlyAffectsInternetCapableNetworks() throws Exception {
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connectWithoutInternet();
waitForIdle();
setIngressRateLimit(1000);
setIngressRateLimit(-1);
final ArrayTrackRecord<Pair<String, Long>>.ReadHead readHeadWifi =
mDeps.mRateLimitHistory.newReadHead();
assertNull(readHeadWifi.poll(TIMEOUT_MS, it -> it.first == wifiLp.getInterfaceName()));
}
@Test
public void testUpdateRateLimit_DisconnectingResetsRateLimit()
throws Exception {
// Steps:
// - connect network
// - set rate limit
// - disconnect network (interface still exists)
// - disable rate limit
// - connect network
// - ensure network interface is not rate limited
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 readHeadWifi =
mDeps.mRateLimitHistory.newReadHead();
int rateLimitInBytesPerSec = 1000;
setIngressRateLimit(rateLimitInBytesPerSec);
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName()
&& it.second == rateLimitInBytesPerSec));
mWiFiNetworkAgent.disconnect();
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName() && it.second == -1));
setIngressRateLimit(-1);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
assertNull(readHeadWifi.poll(TIMEOUT_MS, it -> it.first == wifiLp.getInterfaceName()));
}
@Test
public void testUpdateRateLimit_UpdateExistingRateLimit() 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 readHeadWifi =
mDeps.mRateLimitHistory.newReadHead();
// update an active ingress rate limit
setIngressRateLimit(1000);
setIngressRateLimit(2000);
// verify the following order of execution:
// 1. ingress rate limit set to 1000.
// 2. ingress rate limit disabled (triggered by updating active rate limit).
// 3. ingress rate limit set to 2000.
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName()
&& it.second == 1000));
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName()
&& it.second == -1));
assertNotNull(readHeadWifi.poll(TIMEOUT_MS,
it -> it.first == wifiLp.getInterfaceName()
&& it.second == 2000));
}
}