[NFCT.TETHER.5] Migrate tetherOffloadSetInterfaceQuota from netd to mainline
A preparation for updating BPF map in mainline module. Test: atest TetheringCoverageTests Change-Id: I67dfba750c7303e4aeaf65f5086db1290d176b4d
This commit is contained in:
parent
5ea9c4b64a
commit
67c14b549f
8 changed files with 303 additions and 24 deletions
|
|
@ -92,6 +92,17 @@ public class BpfCoordinatorShimImpl
|
|||
return toTetherStatsValueSparseArray(tetherStatsList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes) {
|
||||
try {
|
||||
mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SparseArray<TetherStatsValue> toTetherStatsValueSparseArray(
|
||||
@NonNull final TetherStatsParcel[] parcels) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.networkstack.tethering.apishim.api31;
|
||||
|
||||
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
||||
|
||||
import android.net.util.SharedLog;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.OsConstants;
|
||||
|
|
@ -29,6 +31,8 @@ import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
|||
import com.android.networkstack.tethering.BpfMap;
|
||||
import com.android.networkstack.tethering.TetherIngressKey;
|
||||
import com.android.networkstack.tethering.TetherIngressValue;
|
||||
import com.android.networkstack.tethering.TetherLimitKey;
|
||||
import com.android.networkstack.tethering.TetherLimitValue;
|
||||
import com.android.networkstack.tethering.TetherStatsKey;
|
||||
import com.android.networkstack.tethering.TetherStatsValue;
|
||||
|
||||
|
|
@ -51,15 +55,20 @@ public class BpfCoordinatorShimImpl
|
|||
@Nullable
|
||||
private final BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||
|
||||
// BPF map of per-interface quota for tethering offload.
|
||||
@Nullable
|
||||
private final BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
|
||||
|
||||
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
|
||||
mLog = deps.getSharedLog().forSubComponent(TAG);
|
||||
mBpfIngressMap = deps.getBpfIngressMap();
|
||||
mBpfStatsMap = deps.getBpfStatsMap();
|
||||
mBpfLimitMap = deps.getBpfLimitMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return mBpfIngressMap != null && mBpfStatsMap != null;
|
||||
return mBpfIngressMap != null && mBpfStatsMap != null && mBpfLimitMap != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -112,12 +121,70 @@ public class BpfCoordinatorShimImpl
|
|||
return tetherStatsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes) {
|
||||
if (!isInitialized()) return false;
|
||||
|
||||
// The common case is an update, where the stats already exist,
|
||||
// hence we read first, even though writing with BPF_NOEXIST
|
||||
// first would make the code simpler.
|
||||
long rxBytes, txBytes;
|
||||
TetherStatsValue statsValue = null;
|
||||
|
||||
try {
|
||||
statsValue = mBpfStatsMap.getValue(new TetherStatsKey(ifIndex));
|
||||
} catch (ErrnoException e) {
|
||||
// The BpfMap#getValue doesn't throw an errno ENOENT exception. Catch other error
|
||||
// while trying to get stats entry.
|
||||
mLog.e("Could not get stats entry of interface index " + ifIndex + ": ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (statsValue != null) {
|
||||
// Ok, there was a stats entry.
|
||||
rxBytes = statsValue.rxBytes;
|
||||
txBytes = statsValue.txBytes;
|
||||
} else {
|
||||
// No stats entry - create one with zeroes.
|
||||
try {
|
||||
// This function is the *only* thing that can create entries.
|
||||
// BpfMap#insertEntry use BPF_NOEXIST to create the entry. The entry is created
|
||||
// if and only if it doesn't exist.
|
||||
mBpfStatsMap.insertEntry(new TetherStatsKey(ifIndex), new TetherStatsValue(
|
||||
0 /* rxPackets */, 0 /* rxBytes */, 0 /* rxErrors */, 0 /* txPackets */,
|
||||
0 /* txBytes */, 0 /* txErrors */));
|
||||
} catch (ErrnoException | IllegalArgumentException e) {
|
||||
mLog.e("Could not create stats entry: ", e);
|
||||
return false;
|
||||
}
|
||||
rxBytes = 0;
|
||||
txBytes = 0;
|
||||
}
|
||||
|
||||
// rxBytes + txBytes won't overflow even at 5gbps for ~936 years.
|
||||
long newLimit = rxBytes + txBytes + quotaBytes;
|
||||
|
||||
// if adding limit (e.g., if limit is QUOTA_UNLIMITED) caused overflow: clamp to 'infinity'
|
||||
if (newLimit < rxBytes + txBytes) newLimit = QUOTA_UNLIMITED;
|
||||
|
||||
try {
|
||||
mBpfLimitMap.updateEntry(new TetherLimitKey(ifIndex), new TetherLimitValue(newLimit));
|
||||
} catch (ErrnoException e) {
|
||||
mLog.e("Fail to set quota " + quotaBytes + " for interface index " + ifIndex + ": ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mBpfIngressMap{"
|
||||
+ (mBpfIngressMap != null ? "initialized" : "not initialized") + "}, "
|
||||
+ "mBpfStatsMap{"
|
||||
+ (mBpfStatsMap != null ? "initialized" : "not initialized") + "} "
|
||||
+ (mBpfStatsMap != null ? "initialized" : "not initialized") + "}, "
|
||||
+ "mBpfLimitMap{"
|
||||
+ (mBpfLimitMap != null ? "initialized" : "not initialized") + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,5 +79,15 @@ public abstract class BpfCoordinatorShim {
|
|||
*/
|
||||
@Nullable
|
||||
public abstract SparseArray<TetherStatsValue> tetherOffloadGetStats();
|
||||
|
||||
/**
|
||||
* Set a per-interface quota for tethering offload.
|
||||
*
|
||||
* @param ifIndex Index of upstream interface
|
||||
* @param quotaBytes The quota defined as the number of bytes, starting from zero and counting
|
||||
* from *now*. A value of QUOTA_UNLIMITED (-1) indicates there is no limit.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ public class BpfCoordinator {
|
|||
"/sys/fs/bpf/map_offload_tether_ingress_map";
|
||||
private static final String TETHER_STATS_MAP_PATH =
|
||||
"/sys/fs/bpf/map_offload_tether_stats_map";
|
||||
private static final String TETHER_LIMIT_MAP_PATH =
|
||||
"/sys/fs/bpf/map_offload_tether_limit_map";
|
||||
|
||||
@VisibleForTesting
|
||||
enum StatsType {
|
||||
|
|
@ -212,6 +214,17 @@ public class BpfCoordinator {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get limit BPF map. */
|
||||
@Nullable public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
|
||||
try {
|
||||
return new BpfMap<>(TETHER_LIMIT_MAP_PATH,
|
||||
BpfMap.BPF_F_RDWR, TetherLimitKey.class, TetherLimitValue.class);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Cannot create limit map: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
@ -673,20 +686,13 @@ public class BpfCoordinator {
|
|||
return quotaBytes;
|
||||
}
|
||||
|
||||
private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) {
|
||||
private boolean sendDataLimitToBpfMap(int ifIndex, long quotaBytes) {
|
||||
if (ifIndex == 0) {
|
||||
Log.wtf(TAG, "Invalid interface index.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return mBpfCoordinatorShim.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
|
||||
}
|
||||
|
||||
// Handle the data limit update from the service which is the stats provider registered for.
|
||||
|
|
@ -699,7 +705,7 @@ public class BpfCoordinator {
|
|||
if (ifIndex == 0) return;
|
||||
|
||||
final long quotaBytes = getQuotaBytes(iface);
|
||||
sendDataLimitToNetd(ifIndex, quotaBytes);
|
||||
sendDataLimitToBpfMap(ifIndex, quotaBytes);
|
||||
}
|
||||
|
||||
// Handle the data limit update while adding forwarding rules.
|
||||
|
|
@ -710,7 +716,7 @@ public class BpfCoordinator {
|
|||
return false;
|
||||
}
|
||||
final long quotaBytes = getQuotaBytes(iface);
|
||||
return sendDataLimitToNetd(ifIndex, quotaBytes);
|
||||
return sendDataLimitToBpfMap(ifIndex, quotaBytes);
|
||||
}
|
||||
|
||||
private boolean isAnyRuleOnUpstream(int upstreamIfindex) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.networkstack.tethering;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
/** The key of BpfMap which is used for tethering per-interface limit. */
|
||||
public class TetherLimitKey extends Struct {
|
||||
@Field(order = 0, type = Type.U32)
|
||||
public final long ifindex; // upstream interface index
|
||||
|
||||
public TetherLimitKey(final long ifindex) {
|
||||
this.ifindex = ifindex;
|
||||
}
|
||||
|
||||
// TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (!(obj instanceof TetherLimitKey)) return false;
|
||||
|
||||
final TetherLimitKey that = (TetherLimitKey) obj;
|
||||
|
||||
return ifindex == that.ifindex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(ifindex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ifindex: %d", ifindex);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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.networkstack.tethering;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
/** The value of BpfMap which is used for tethering per-interface limit. */
|
||||
public class TetherLimitValue extends Struct {
|
||||
// Use the signed long variable to store the int64 limit on limit BPF map.
|
||||
// S64 is enough for each interface limit even at 5Gbps for ~468 years.
|
||||
// 2^63 / (5 * 1000 * 1000 * 1000) * 8 / 86400 / 365 = 468.
|
||||
// Note that QUOTA_UNLIMITED (-1) indicates there is no limit.
|
||||
@Field(order = 0, type = Type.S64)
|
||||
public final long limit;
|
||||
|
||||
public TetherLimitValue(final long limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
// TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (!(obj instanceof TetherLimitValue)) return false;
|
||||
|
||||
final TetherLimitValue that = (TetherLimitValue) obj;
|
||||
|
||||
return limit == that.limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("limit: %d", limit);
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +106,8 @@ import com.android.networkstack.tethering.BpfMap;
|
|||
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||
import com.android.networkstack.tethering.TetherIngressKey;
|
||||
import com.android.networkstack.tethering.TetherIngressValue;
|
||||
import com.android.networkstack.tethering.TetherLimitKey;
|
||||
import com.android.networkstack.tethering.TetherLimitValue;
|
||||
import com.android.networkstack.tethering.TetherStatsKey;
|
||||
import com.android.networkstack.tethering.TetherStatsValue;
|
||||
import com.android.networkstack.tethering.TetheringConfiguration;
|
||||
|
|
@ -172,6 +174,7 @@ public class IpServerTest {
|
|||
@Mock private TetheringConfiguration mTetherConfig;
|
||||
@Mock private BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
|
||||
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
|
||||
|
||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||
|
||||
|
|
@ -301,6 +304,11 @@ public class IpServerTest {
|
|||
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
|
||||
return mBpfStatsMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
|
||||
return mBpfLimitMap;
|
||||
}
|
||||
};
|
||||
mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,23 @@ public class BpfCoordinatorTest {
|
|||
public void updateEntry(K key, V value) throws ErrnoException {
|
||||
mMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertEntry(K key, V value) throws ErrnoException,
|
||||
IllegalArgumentException {
|
||||
// The entry is created if and only if it doesn't exist. See BpfMap#insertEntry.
|
||||
if (mMap.get(key) != null) {
|
||||
throw new IllegalArgumentException(key + " already exist");
|
||||
}
|
||||
mMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue(@NonNull K key) throws ErrnoException {
|
||||
// Return value for a given key. Otherwise, return null without an error ENOENT.
|
||||
// BpfMap#getValue treats that the entry is not found as no error.
|
||||
return mMap.get(key);
|
||||
}
|
||||
};
|
||||
|
||||
@Mock private NetworkStatsManager mStatsManager;
|
||||
|
|
@ -142,6 +159,8 @@ public class BpfCoordinatorTest {
|
|||
private final TestLooper mTestLooper = new TestLooper();
|
||||
private final TestBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
|
||||
spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
|
||||
private final TestBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
|
||||
spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class));
|
||||
private BpfCoordinator.Dependencies mDeps =
|
||||
spy(new BpfCoordinator.Dependencies() {
|
||||
@NonNull
|
||||
|
|
@ -178,6 +197,11 @@ public class BpfCoordinatorTest {
|
|||
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
|
||||
return mBpfStatsMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
|
||||
return mBpfLimitMap;
|
||||
}
|
||||
});
|
||||
|
||||
@Before public void setUp() {
|
||||
|
|
@ -325,6 +349,34 @@ public class BpfCoordinatorTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void verifyTetherOffloadSetInterfaceQuota(@Nullable InOrder inOrder, int ifIndex,
|
||||
long quotaBytes, boolean isInit) throws Exception {
|
||||
if (mDeps.isAtLeastS()) {
|
||||
final TetherStatsKey key = new TetherStatsKey(ifIndex);
|
||||
verifyWithOrder(inOrder, mBpfStatsMap).getValue(key);
|
||||
if (isInit) {
|
||||
verifyWithOrder(inOrder, mBpfStatsMap).insertEntry(key, new TetherStatsValue(
|
||||
0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */,
|
||||
0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */));
|
||||
}
|
||||
verifyWithOrder(inOrder, mBpfLimitMap).updateEntry(new TetherLimitKey(ifIndex),
|
||||
new TetherLimitValue(quotaBytes));
|
||||
} else {
|
||||
verifyWithOrder(inOrder, mNetd).tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyNeverTetherOffloadSetInterfaceQuota(@Nullable InOrder inOrder)
|
||||
throws Exception {
|
||||
if (mDeps.isAtLeastS()) {
|
||||
inOrder.verify(mBpfStatsMap, never()).getValue(any());
|
||||
inOrder.verify(mBpfStatsMap, never()).insertEntry(any(), any());
|
||||
inOrder.verify(mBpfLimitMap, never()).updateEntry(any(), any());
|
||||
} else {
|
||||
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||
}
|
||||
}
|
||||
|
||||
// S+ and R api minimum tests.
|
||||
// The following tests are used to provide minimum checking for the APIs on different flow.
|
||||
// The auto merge is not enabled on mainline prod. The code flow R may be verified at the
|
||||
|
|
@ -347,6 +399,8 @@ public class BpfCoordinatorTest {
|
|||
final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
||||
verifyTetherOffloadRuleAdd(null, rule);
|
||||
verifyTetherOffloadSetInterfaceQuota(null, mobileIfIndex, QUOTA_UNLIMITED,
|
||||
true /* isInit */);
|
||||
|
||||
// Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
|
||||
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||
|
|
@ -596,10 +650,11 @@ public class BpfCoordinatorTest {
|
|||
// Set the unlimited quota as default if the service has never applied a data limit for a
|
||||
// given upstream. Note that the data limit only be applied on an upstream which has rules.
|
||||
final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap);
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap, mBpfLimitMap, mBpfStatsMap);
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
||||
verifyTetherOffloadRuleAdd(inOrder, rule);
|
||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
||||
verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED,
|
||||
true /* isInit */);
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
// [2] Specific limit.
|
||||
|
|
@ -607,7 +662,8 @@ public class BpfCoordinatorTest {
|
|||
for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) {
|
||||
mTetherStatsProvider.onSetLimit(mobileIface, quota);
|
||||
waitForIdle();
|
||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota);
|
||||
verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, quota,
|
||||
false /* isInit */);
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
|
|
@ -637,28 +693,28 @@ public class BpfCoordinatorTest {
|
|||
// Applying a data limit to the current upstream does not take any immediate action.
|
||||
// The data limit could be only set on an upstream which has rules.
|
||||
final long limit = 12345;
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap);
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap, mBpfLimitMap, mBpfStatsMap);
|
||||
mTetherStatsProvider.onSetLimit(mobileIface, limit);
|
||||
waitForIdle();
|
||||
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||
|
||||
// Adding the first rule on current upstream immediately sends the quota to netd.
|
||||
final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, ruleA);
|
||||
verifyTetherOffloadRuleAdd(inOrder, ruleA);
|
||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit);
|
||||
verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, limit, true /* isInit */);
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
// Adding the second rule on current upstream does not send the quota to netd.
|
||||
final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B);
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, ruleB);
|
||||
verifyTetherOffloadRuleAdd(inOrder, ruleB);
|
||||
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||
|
||||
// Removing the second rule on current upstream does not send the quota to netd.
|
||||
coordinator.tetherOffloadRuleRemove(mIpServer, ruleB);
|
||||
verifyTetherOffloadRuleRemove(inOrder, ruleB);
|
||||
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||
verifyNeverTetherOffloadSetInterfaceQuota(inOrder);
|
||||
|
||||
// Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
|
||||
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||
|
|
@ -682,7 +738,7 @@ public class BpfCoordinatorTest {
|
|||
coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface);
|
||||
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
|
||||
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap);
|
||||
final InOrder inOrder = inOrder(mNetd, mBpfIngressMap, mBpfLimitMap, mBpfStatsMap);
|
||||
|
||||
// Before the rule test, here are the additional actions while the rules are changed.
|
||||
// - After adding the first rule on a given upstream, the coordinator adds a data limit.
|
||||
|
|
@ -700,7 +756,8 @@ public class BpfCoordinatorTest {
|
|||
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA);
|
||||
verifyTetherOffloadRuleAdd(inOrder, ethernetRuleA);
|
||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED);
|
||||
verifyTetherOffloadSetInterfaceQuota(inOrder, ethIfIndex, QUOTA_UNLIMITED,
|
||||
true /* isInit */);
|
||||
|
||||
coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB);
|
||||
verifyTetherOffloadRuleAdd(inOrder, ethernetRuleB);
|
||||
|
|
@ -718,7 +775,8 @@ public class BpfCoordinatorTest {
|
|||
coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
|
||||
verifyTetherOffloadRuleRemove(inOrder, ethernetRuleA);
|
||||
verifyTetherOffloadRuleAdd(inOrder, mobileRuleA);
|
||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
||||
verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED,
|
||||
true /* isInit */);
|
||||
verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB);
|
||||
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex);
|
||||
verifyTetherOffloadRuleAdd(inOrder, mobileRuleB);
|
||||
|
|
@ -825,6 +883,15 @@ public class BpfCoordinatorTest {
|
|||
checkBpfDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
@IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
public void testBpfDisabledbyNoBpfLimitMap() throws Exception {
|
||||
setupFunctioningNetdInterface();
|
||||
doReturn(null).when(mDeps).getBpfLimitMap();
|
||||
|
||||
checkBpfDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTetheringConfigSetPollingInterval() throws Exception {
|
||||
setupFunctioningNetdInterface();
|
||||
|
|
|
|||
Loading…
Reference in a new issue