Merge changes I969d6182,Ie73f7b4d am: 2668d4a0e2
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1537801 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ie425332418fd3cff8e58de9f553703ba67d87641
This commit is contained in:
@@ -17,14 +17,18 @@
|
|||||||
package com.android.networkstack.tethering.apishim.api30;
|
package com.android.networkstack.tethering.apishim.api30;
|
||||||
|
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
|
import android.net.TetherStatsParcel;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
|
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bpf coordinator class for API shims.
|
* Bpf coordinator class for API shims.
|
||||||
@@ -60,6 +64,47 @@ public class BpfCoordinatorShimImpl
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) {
|
||||||
|
try {
|
||||||
|
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
mLog.e("Could not remove IPv6 forwarding rule: ", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
|
||||||
|
final TetherStatsParcel[] tetherStatsList;
|
||||||
|
try {
|
||||||
|
// The reported tether stats are total data usage for all currently-active upstream
|
||||||
|
// interfaces since tethering start. There will only ever be one entry for a given
|
||||||
|
// interface index.
|
||||||
|
tetherStatsList = mNetd.tetherOffloadGetStats();
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
mLog.e("Fail to fetch tethering stats from netd: " + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toTetherStatsValueSparseArray(tetherStatsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private SparseArray<TetherStatsValue> toTetherStatsValueSparseArray(
|
||||||
|
@NonNull final TetherStatsParcel[] parcels) {
|
||||||
|
final SparseArray<TetherStatsValue> tetherStatsList = new SparseArray<TetherStatsValue>();
|
||||||
|
|
||||||
|
for (TetherStatsParcel p : parcels) {
|
||||||
|
tetherStatsList.put(p.ifIndex, new TetherStatsValue(p.rxPackets, p.rxBytes,
|
||||||
|
0 /* rxErrors */, p.txPackets, p.txBytes, 0 /* txErrors */));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tetherStatsList;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Netd used";
|
return "Netd used";
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.networkstack.tethering.apishim.api31;
|
|||||||
|
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
|
import android.system.OsConstants;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -27,6 +29,8 @@ import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
|||||||
import com.android.networkstack.tethering.BpfMap;
|
import com.android.networkstack.tethering.BpfMap;
|
||||||
import com.android.networkstack.tethering.TetherIngressKey;
|
import com.android.networkstack.tethering.TetherIngressKey;
|
||||||
import com.android.networkstack.tethering.TetherIngressValue;
|
import com.android.networkstack.tethering.TetherIngressValue;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsKey;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bpf coordinator class for API shims.
|
* Bpf coordinator class for API shims.
|
||||||
@@ -43,14 +47,19 @@ public class BpfCoordinatorShimImpl
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
|
private final BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
|
||||||
|
|
||||||
|
// BPF map of tethering statistics of the upstream interface since tethering startup.
|
||||||
|
@Nullable
|
||||||
|
private final BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||||
|
|
||||||
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
|
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
|
||||||
mLog = deps.getSharedLog().forSubComponent(TAG);
|
mLog = deps.getSharedLog().forSubComponent(TAG);
|
||||||
mBpfIngressMap = deps.getBpfIngressMap();
|
mBpfIngressMap = deps.getBpfIngressMap();
|
||||||
|
mBpfStatsMap = deps.getBpfStatsMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
return mBpfIngressMap != null;
|
return mBpfIngressMap != null && mBpfStatsMap != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,10 +79,45 @@ public class BpfCoordinatorShimImpl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) {
|
||||||
|
if (!isInitialized()) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mBpfIngressMap.deleteEntry(rule.makeTetherIngressKey());
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
// Silent if the rule did not exist.
|
||||||
|
if (e.errno != OsConstants.ENOENT) {
|
||||||
|
mLog.e("Could not update entry: ", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
|
||||||
|
if (!isInitialized()) return null;
|
||||||
|
|
||||||
|
final SparseArray<TetherStatsValue> tetherStatsList = new SparseArray<TetherStatsValue>();
|
||||||
|
try {
|
||||||
|
// The reported tether stats are total data usage for all currently-active upstream
|
||||||
|
// interfaces since tethering start.
|
||||||
|
mBpfStatsMap.forEach((key, value) -> tetherStatsList.put((int) key.ifindex, value));
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
mLog.e("Fail to fetch tethering stats from BPF map: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return tetherStatsList;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "mBpfIngressMap{"
|
return "mBpfIngressMap{"
|
||||||
+ (mBpfIngressMap != null ? "initialized" : "not initialized") + "} "
|
+ (mBpfIngressMap != null ? "initialized" : "not initialized") + "}, "
|
||||||
|
+ "mBpfStatsMap{"
|
||||||
|
+ (mBpfStatsMap != null ? "initialized" : "not initialized") + "} "
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,14 @@
|
|||||||
|
|
||||||
package com.android.networkstack.tethering.apishim.common;
|
package com.android.networkstack.tethering.apishim.common;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
|
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bpf coordinator class for API shims.
|
* Bpf coordinator class for API shims.
|
||||||
@@ -54,5 +58,26 @@ public abstract class BpfCoordinatorShim {
|
|||||||
* @param rule The rule to add or update.
|
* @param rule The rule to add or update.
|
||||||
*/
|
*/
|
||||||
public abstract boolean tetherOffloadRuleAdd(@NonNull Ipv6ForwardingRule rule);
|
public abstract boolean tetherOffloadRuleAdd(@NonNull Ipv6ForwardingRule rule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a tethering offload rule from the BPF map.
|
||||||
|
*
|
||||||
|
* Currently, only downstream /128 IPv6 entries are supported. An existing rule will be deleted
|
||||||
|
* if the destination IP address and the source interface match. It is not an error if there is
|
||||||
|
* no matching rule to delete.
|
||||||
|
*
|
||||||
|
* @param rule The rule to delete.
|
||||||
|
*/
|
||||||
|
public abstract boolean tetherOffloadRuleRemove(@NonNull Ipv6ForwardingRule rule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return BPF tethering offload statistics.
|
||||||
|
*
|
||||||
|
* @return an array of TetherStatsValue's, where each entry contains the upstream interface
|
||||||
|
* index and its tethering statistics since tethering was first started.
|
||||||
|
* There will only ever be one entry for a given interface index.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public abstract SparseArray<TetherStatsValue> tetherOffloadGetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.networkstack.tethering.TetherStatsValue;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
@@ -91,6 +93,13 @@ public class TetheringUtils {
|
|||||||
txPackets = tetherStats.txPackets;
|
txPackets = tetherStats.txPackets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ForwardedStats(@NonNull TetherStatsValue tetherStats) {
|
||||||
|
rxBytes = tetherStats.rxBytes;
|
||||||
|
rxPackets = tetherStats.rxPackets;
|
||||||
|
txBytes = tetherStats.txBytes;
|
||||||
|
txPackets = tetherStats.txPackets;
|
||||||
|
}
|
||||||
|
|
||||||
public ForwardedStats(@NonNull ForwardedStats other) {
|
public ForwardedStats(@NonNull ForwardedStats other) {
|
||||||
rxBytes = other.rxBytes;
|
rxBytes = other.rxBytes;
|
||||||
rxPackets = other.rxPackets;
|
rxPackets = other.rxPackets;
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ public class BpfCoordinator {
|
|||||||
private static final int DUMP_TIMEOUT_MS = 10_000;
|
private static final int DUMP_TIMEOUT_MS = 10_000;
|
||||||
private static final String TETHER_INGRESS_FS_PATH =
|
private static final String TETHER_INGRESS_FS_PATH =
|
||||||
"/sys/fs/bpf/map_offload_tether_ingress_map";
|
"/sys/fs/bpf/map_offload_tether_ingress_map";
|
||||||
|
private static final String TETHER_STATS_MAP_PATH =
|
||||||
|
"/sys/fs/bpf/map_offload_tether_stats_map";
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
enum StatsType {
|
enum StatsType {
|
||||||
@@ -157,7 +159,7 @@ public class BpfCoordinator {
|
|||||||
|
|
||||||
// Runnable that used by scheduling next polling of stats.
|
// Runnable that used by scheduling next polling of stats.
|
||||||
private final Runnable mScheduledPollingTask = () -> {
|
private final Runnable mScheduledPollingTask = () -> {
|
||||||
updateForwardedStatsFromNetd();
|
updateForwardedStats();
|
||||||
maybeSchedulePollingStats();
|
maybeSchedulePollingStats();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,6 +201,17 @@ public class BpfCoordinator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get stats BPF map. */
|
||||||
|
@Nullable public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
|
||||||
|
try {
|
||||||
|
return new BpfMap<>(TETHER_STATS_MAP_PATH,
|
||||||
|
BpfMap.BPF_F_RDWR, TetherStatsKey.class, TetherStatsValue.class);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
Log.e(TAG, "Cannot create stats map: " + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -261,7 +274,7 @@ public class BpfCoordinator {
|
|||||||
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
|
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
|
||||||
mHandler.removeCallbacks(mScheduledPollingTask);
|
mHandler.removeCallbacks(mScheduledPollingTask);
|
||||||
}
|
}
|
||||||
updateForwardedStatsFromNetd();
|
updateForwardedStats();
|
||||||
mPollingStarted = false;
|
mPollingStarted = false;
|
||||||
|
|
||||||
mLog.i("Polling stopped");
|
mLog.i("Polling stopped");
|
||||||
@@ -316,13 +329,7 @@ public class BpfCoordinator {
|
|||||||
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
||||||
if (!isUsingBpf()) return;
|
if (!isUsingBpf()) return;
|
||||||
|
|
||||||
try {
|
if (!mBpfCoordinatorShim.tetherOffloadRuleRemove(rule)) return;
|
||||||
// TODO: Perhaps avoid to remove a non-existent rule.
|
|
||||||
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
|
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
|
||||||
mLog.e("Could not remove IPv6 forwarding rule: ", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
|
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
|
||||||
if (rules == null) return;
|
if (rules == null) return;
|
||||||
@@ -344,8 +351,14 @@ public class BpfCoordinator {
|
|||||||
try {
|
try {
|
||||||
final TetherStatsParcel stats =
|
final TetherStatsParcel stats =
|
||||||
mNetd.tetherOffloadGetAndClearStats(upstreamIfindex);
|
mNetd.tetherOffloadGetAndClearStats(upstreamIfindex);
|
||||||
|
SparseArray<TetherStatsValue> tetherStatsList =
|
||||||
|
new SparseArray<TetherStatsValue>();
|
||||||
|
tetherStatsList.put(stats.ifIndex, new TetherStatsValue(stats.rxPackets,
|
||||||
|
stats.rxBytes, 0 /* rxErrors */, stats.txPackets, stats.txBytes,
|
||||||
|
0 /* txErrors */));
|
||||||
|
|
||||||
// Update the last stats delta and delete the local cache for a given upstream.
|
// Update the last stats delta and delete the local cache for a given upstream.
|
||||||
updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats});
|
updateQuotaAndStatsFromSnapshot(tetherStatsList);
|
||||||
mStats.remove(upstreamIfindex);
|
mStats.remove(upstreamIfindex);
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
Log.wtf(TAG, "Exception when cleanup tether stats for upstream index "
|
Log.wtf(TAG, "Exception when cleanup tether stats for upstream index "
|
||||||
@@ -743,10 +756,11 @@ public class BpfCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateQuotaAndStatsFromSnapshot(
|
private void updateQuotaAndStatsFromSnapshot(
|
||||||
@NonNull final TetherStatsParcel[] tetherStatsList) {
|
@NonNull final SparseArray<TetherStatsValue> tetherStatsList) {
|
||||||
long usedAlertQuota = 0;
|
long usedAlertQuota = 0;
|
||||||
for (TetherStatsParcel tetherStats : tetherStatsList) {
|
for (int i = 0; i < tetherStatsList.size(); i++) {
|
||||||
final Integer ifIndex = tetherStats.ifIndex;
|
final Integer ifIndex = tetherStatsList.keyAt(i);
|
||||||
|
final TetherStatsValue tetherStats = tetherStatsList.valueAt(i);
|
||||||
final ForwardedStats curr = new ForwardedStats(tetherStats);
|
final ForwardedStats curr = new ForwardedStats(tetherStats);
|
||||||
final ForwardedStats base = mStats.get(ifIndex);
|
final ForwardedStats base = mStats.get(ifIndex);
|
||||||
final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
|
final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
|
||||||
@@ -778,16 +792,15 @@ public class BpfCoordinator {
|
|||||||
// TODO: Count the used limit quota for notifying data limit reached.
|
// TODO: Count the used limit quota for notifying data limit reached.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateForwardedStatsFromNetd() {
|
private void updateForwardedStats() {
|
||||||
final TetherStatsParcel[] tetherStatsList;
|
final SparseArray<TetherStatsValue> tetherStatsList =
|
||||||
try {
|
mBpfCoordinatorShim.tetherOffloadGetStats();
|
||||||
// The reported tether stats are total data usage for all currently-active upstream
|
|
||||||
// interfaces since tethering start.
|
if (tetherStatsList == null) {
|
||||||
tetherStatsList = mNetd.tetherOffloadGetStats();
|
mLog.e("Problem fetching tethering stats");
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
|
||||||
mLog.e("Problem fetching tethering stats: ", e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateQuotaAndStatsFromSnapshot(tetherStatsList);
|
updateQuotaAndStatsFromSnapshot(tetherStatsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.system.ErrnoException;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.net.module.util.Struct;
|
import com.android.net.module.util.Struct;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -76,6 +77,21 @@ public class BpfMap<K extends Struct, V extends Struct> implements AutoCloseable
|
|||||||
mValueSize = Struct.getSize(value);
|
mValueSize = Struct.getSize(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for testing only.
|
||||||
|
* The derived class implements an internal mocked map. It need to implement all functions
|
||||||
|
* which are related with the native BPF map because the BPF map handler is not initialized.
|
||||||
|
* See BpfCoordinatorTest#TestBpfMap.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
protected BpfMap(final Class<K> key, final Class<V> value) {
|
||||||
|
mMapFd = -1;
|
||||||
|
mKeyClass = key;
|
||||||
|
mValueClass = value;
|
||||||
|
mKeySize = Struct.getSize(key);
|
||||||
|
mValueSize = Struct.getSize(value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update an existing or create a new key -> value entry in an eBbpf map.
|
* Update an existing or create a new key -> value entry in an eBbpf map.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 stats. */
|
||||||
|
public class TetherStatsKey extends Struct {
|
||||||
|
@Field(order = 0, type = Type.U32)
|
||||||
|
public final long ifindex; // upstream interface index
|
||||||
|
|
||||||
|
public TetherStatsKey(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 TetherStatsKey)) return false;
|
||||||
|
|
||||||
|
final TetherStatsKey that = (TetherStatsKey) 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,80 @@
|
|||||||
|
/*
|
||||||
|
* 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 stats. */
|
||||||
|
public class TetherStatsValue extends Struct {
|
||||||
|
// Use the signed long variable to store the uint64 stats from stats BPF map.
|
||||||
|
// U63 is enough for each data element even at 5Gbps for ~468 years.
|
||||||
|
// 2^63 / (5 * 1000 * 1000 * 1000) * 8 / 86400 / 365 = 468.
|
||||||
|
@Field(order = 0, type = Type.U63)
|
||||||
|
public final long rxPackets;
|
||||||
|
@Field(order = 1, type = Type.U63)
|
||||||
|
public final long rxBytes;
|
||||||
|
@Field(order = 2, type = Type.U63)
|
||||||
|
public final long rxErrors;
|
||||||
|
@Field(order = 3, type = Type.U63)
|
||||||
|
public final long txPackets;
|
||||||
|
@Field(order = 4, type = Type.U63)
|
||||||
|
public final long txBytes;
|
||||||
|
@Field(order = 5, type = Type.U63)
|
||||||
|
public final long txErrors;
|
||||||
|
|
||||||
|
public TetherStatsValue(final long rxPackets, final long rxBytes, final long rxErrors,
|
||||||
|
final long txPackets, final long txBytes, final long txErrors) {
|
||||||
|
this.rxPackets = rxPackets;
|
||||||
|
this.rxBytes = rxBytes;
|
||||||
|
this.rxErrors = rxErrors;
|
||||||
|
this.txPackets = txPackets;
|
||||||
|
this.txBytes = txBytes;
|
||||||
|
this.txErrors = txErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 TetherStatsValue)) return false;
|
||||||
|
|
||||||
|
final TetherStatsValue that = (TetherStatsValue) obj;
|
||||||
|
|
||||||
|
return rxPackets == that.rxPackets
|
||||||
|
&& rxBytes == that.rxBytes
|
||||||
|
&& rxErrors == that.rxErrors
|
||||||
|
&& txPackets == that.txPackets
|
||||||
|
&& txBytes == that.txBytes
|
||||||
|
&& txErrors == that.txErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Long.hashCode(rxPackets) ^ Long.hashCode(rxBytes) ^ Long.hashCode(rxErrors)
|
||||||
|
^ Long.hashCode(txPackets) ^ Long.hashCode(txBytes) ^ Long.hashCode(txErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("rxPackets: %s, rxBytes: %s, rxErrors: %s, txPackets: %s, "
|
||||||
|
+ "txBytes: %s, txErrors: %s", rxPackets, rxBytes, rxErrors, txPackets,
|
||||||
|
txBytes, txErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,8 @@ import com.android.networkstack.tethering.BpfMap;
|
|||||||
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||||
import com.android.networkstack.tethering.TetherIngressKey;
|
import com.android.networkstack.tethering.TetherIngressKey;
|
||||||
import com.android.networkstack.tethering.TetherIngressValue;
|
import com.android.networkstack.tethering.TetherIngressValue;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsKey;
|
||||||
|
import com.android.networkstack.tethering.TetherStatsValue;
|
||||||
import com.android.networkstack.tethering.TetheringConfiguration;
|
import com.android.networkstack.tethering.TetheringConfiguration;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||||
@@ -169,6 +171,7 @@ public class IpServerTest {
|
|||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
@Mock private TetheringConfiguration mTetherConfig;
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
@Mock private BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
|
@Mock private BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
|
||||||
|
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||||
|
|
||||||
@@ -293,6 +296,11 @@ public class IpServerTest {
|
|||||||
public BpfMap<TetherIngressKey, TetherIngressValue> getBpfIngressMap() {
|
public BpfMap<TetherIngressKey, TetherIngressValue> getBpfIngressMap() {
|
||||||
return mBpfIngressMap;
|
return mBpfIngressMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
|
||||||
|
return mBpfStatsMap;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
|
mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
|
||||||
|
|
||||||
@@ -802,6 +810,28 @@ public class IpServerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyTetherOffloadRuleRemove(@Nullable InOrder inOrder, int upstreamIfindex,
|
||||||
|
@NonNull final InetAddress dst, @NonNull final MacAddress dstMac) throws Exception {
|
||||||
|
if (mBpfDeps.isAtLeastS()) {
|
||||||
|
verifyWithOrder(inOrder, mBpfIngressMap).deleteEntry(makeIngressKey(upstreamIfindex,
|
||||||
|
dst));
|
||||||
|
} else {
|
||||||
|
// |dstMac| is not required for deleting rules. Used bacause tetherOffloadRuleRemove
|
||||||
|
// uses a whole rule to be a argument.
|
||||||
|
// See system/netd/server/TetherController.cpp/TetherController#removeOffloadRule.
|
||||||
|
verifyWithOrder(inOrder, mNetd).tetherOffloadRuleRemove(matches(upstreamIfindex, dst,
|
||||||
|
dstMac));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyNeverTetherOffloadRuleRemove() throws Exception {
|
||||||
|
if (mBpfDeps.isAtLeastS()) {
|
||||||
|
verify(mBpfIngressMap, never()).deleteEntry(any());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
|
private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
|
||||||
TetherStatsParcel parcel = new TetherStatsParcel();
|
TetherStatsParcel parcel = new TetherStatsParcel();
|
||||||
@@ -869,14 +899,14 @@ public class IpServerTest {
|
|||||||
recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
|
recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
||||||
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull));
|
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull));
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighA, macNull);
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
|
|
||||||
// A neighbor that is deleted causes the rule to be removed.
|
// A neighbor that is deleted causes the rule to be removed.
|
||||||
recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
|
recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
||||||
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull));
|
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull));
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macNull);
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
|
|
||||||
// Upstream changes result in updating the rules.
|
// Upstream changes result in updating the rules.
|
||||||
@@ -889,9 +919,9 @@ public class IpServerTest {
|
|||||||
lp.setInterfaceName(UPSTREAM_IFACE2);
|
lp.setInterfaceName(UPSTREAM_IFACE2);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2);
|
verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
|
verifyTetherOffloadRuleRemove(inOrder, UPSTREAM_IFINDEX, neighA, macA);
|
||||||
verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighA, macA);
|
verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighA, macA);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
|
verifyTetherOffloadRuleRemove(inOrder, UPSTREAM_IFINDEX, neighB, macB);
|
||||||
verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighB, macB);
|
verifyTetherOffloadRuleAdd(inOrder, UPSTREAM_IFINDEX2, neighB, macB);
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
|
|
||||||
@@ -902,8 +932,8 @@ public class IpServerTest {
|
|||||||
// - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost.
|
// - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost.
|
||||||
// See dispatchTetherConnectionChanged.
|
// See dispatchTetherConnectionChanged.
|
||||||
verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer);
|
verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer);
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX2, neighA, macA);
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX2, neighB, macB);
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
|
|
||||||
// If the upstream is IPv4-only, no rules are added.
|
// If the upstream is IPv4-only, no rules are added.
|
||||||
@@ -929,7 +959,7 @@ public class IpServerTest {
|
|||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
|
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macB);
|
||||||
|
|
||||||
// When the interface goes down, rules are removed.
|
// When the interface goes down, rules are removed.
|
||||||
lp.setInterfaceName(UPSTREAM_IFACE);
|
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||||
@@ -947,8 +977,8 @@ public class IpServerTest {
|
|||||||
mIpServer.stop();
|
mIpServer.stop();
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
|
verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighA, macA);
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neighB, macB);
|
||||||
verify(mIpNeighborMonitor).stop();
|
verify(mIpNeighborMonitor).stop();
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
}
|
}
|
||||||
@@ -982,7 +1012,7 @@ public class IpServerTest {
|
|||||||
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
|
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
|
||||||
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
verify(mBpfCoordinator).tetherOffloadRuleRemove(
|
||||||
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull));
|
mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull));
|
||||||
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
|
verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, neigh, macNull);
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
|
|
||||||
// [2] Disable BPF offload.
|
// [2] Disable BPF offload.
|
||||||
@@ -998,7 +1028,7 @@ public class IpServerTest {
|
|||||||
|
|
||||||
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
|
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
|
||||||
verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any());
|
verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any());
|
||||||
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
verifyNeverTetherOffloadRuleRemove();
|
||||||
resetNetdBpfMapAndCoordinator();
|
resetNetdBpfMapAndCoordinator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import android.net.util.SharedLog;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -68,6 +69,7 @@ import androidx.test.filters.SmallTest;
|
|||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.net.module.util.NetworkStackConstants;
|
import com.android.net.module.util.NetworkStackConstants;
|
||||||
|
import com.android.net.module.util.Struct;
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||||
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
|
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
|
||||||
@@ -85,7 +87,10 @@ import java.net.Inet6Address;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -97,6 +102,32 @@ public class BpfCoordinatorTest {
|
|||||||
private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
|
private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
|
||||||
private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
|
private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
|
||||||
|
|
||||||
|
// The test fake BPF map class is needed because the test has no privilege to access the BPF
|
||||||
|
// map. All member functions which eventually call JNI to access the real native BPF map need
|
||||||
|
// to be overridden.
|
||||||
|
// TODO: consider moving to an individual file.
|
||||||
|
private class TestBpfMap<K extends Struct, V extends Struct> extends BpfMap<K, V> {
|
||||||
|
private final HashMap<K, V> mMap = new HashMap<K, V>();
|
||||||
|
|
||||||
|
TestBpfMap(final Class<K> key, final Class<V> value) {
|
||||||
|
super(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(BiConsumer<K, V> action) throws ErrnoException {
|
||||||
|
// TODO: consider using mocked #getFirstKey and #getNextKey to iterate. It helps to
|
||||||
|
// implement the entry deletion in the iteration if required.
|
||||||
|
for (Map.Entry<K, V> entry : mMap.entrySet()) {
|
||||||
|
action.accept(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateEntry(K key, V value) throws ErrnoException {
|
||||||
|
mMap.put(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
@Mock private IpServer mIpServer;
|
@Mock private IpServer mIpServer;
|
||||||
@@ -109,6 +140,8 @@ public class BpfCoordinatorTest {
|
|||||||
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
|
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
|
||||||
ArgumentCaptor.forClass(ArrayList.class);
|
ArgumentCaptor.forClass(ArrayList.class);
|
||||||
private final TestLooper mTestLooper = new TestLooper();
|
private final TestLooper mTestLooper = new TestLooper();
|
||||||
|
private final TestBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
|
||||||
|
spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
|
||||||
private BpfCoordinator.Dependencies mDeps =
|
private BpfCoordinator.Dependencies mDeps =
|
||||||
spy(new BpfCoordinator.Dependencies() {
|
spy(new BpfCoordinator.Dependencies() {
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -140,6 +173,11 @@ public class BpfCoordinatorTest {
|
|||||||
public BpfMap<TetherIngressKey, TetherIngressValue> getBpfIngressMap() {
|
public BpfMap<TetherIngressKey, TetherIngressValue> getBpfIngressMap() {
|
||||||
return mBpfIngressMap;
|
return mBpfIngressMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
|
||||||
|
return mBpfStatsMap;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@Before public void setUp() {
|
@Before public void setUp() {
|
||||||
@@ -190,14 +228,44 @@ public class BpfCoordinatorTest {
|
|||||||
return parcel;
|
return parcel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up specific tether stats list and wait for the stats cache is updated by polling thread
|
// Update a stats entry or create if not exists.
|
||||||
|
private void updateStatsEntry(@NonNull TetherStatsParcel stats) throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
final TetherStatsKey key = new TetherStatsKey(stats.ifIndex);
|
||||||
|
final TetherStatsValue value = new TetherStatsValue(stats.rxPackets, stats.rxBytes,
|
||||||
|
0L /* rxErrors */, stats.txPackets, stats.txBytes, 0L /* txErrors */);
|
||||||
|
mBpfStatsMap.updateEntry(key, value);
|
||||||
|
} else {
|
||||||
|
when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[] {stats});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update specific tether stats list and wait for the stats cache is updated by polling thread
|
||||||
// in the coordinator. Beware of that it is only used for the default polling interval.
|
// in the coordinator. Beware of that it is only used for the default polling interval.
|
||||||
private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
|
// Note that the mocked tetherOffloadGetStats of netd replaces all stats entries because it
|
||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
|
// doesn't store the previous entries.
|
||||||
|
private void updateStatsEntriesAndWaitForUpdate(@NonNull TetherStatsParcel[] tetherStatsList)
|
||||||
|
throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
for (TetherStatsParcel stats : tetherStatsList) {
|
||||||
|
updateStatsEntry(stats);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
|
||||||
|
}
|
||||||
|
|
||||||
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearStatsInvocations() {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
clearInvocations(mBpfStatsMap);
|
||||||
|
} else {
|
||||||
|
clearInvocations(mNetd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
|
private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
|
||||||
if (inOrder != null) {
|
if (inOrder != null) {
|
||||||
return inOrder.verify(t);
|
return inOrder.verify(t);
|
||||||
@@ -206,6 +274,22 @@ public class BpfCoordinatorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyTetherOffloadGetStats() throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
verify(mBpfStatsMap).forEach(any());
|
||||||
|
} else {
|
||||||
|
verify(mNetd).tetherOffloadGetStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyNeverTetherOffloadGetStats() throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
verify(mBpfStatsMap, never()).forEach(any());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, never()).tetherOffloadGetStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder,
|
private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder,
|
||||||
@NonNull Ipv6ForwardingRule rule) throws Exception {
|
@NonNull Ipv6ForwardingRule rule) throws Exception {
|
||||||
if (mDeps.isAtLeastS()) {
|
if (mDeps.isAtLeastS()) {
|
||||||
@@ -224,8 +308,30 @@ public class BpfCoordinatorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyTetherOffloadRuleRemove(@Nullable InOrder inOrder,
|
||||||
|
@NonNull final Ipv6ForwardingRule rule) throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
verifyWithOrder(inOrder, mBpfIngressMap).deleteEntry(rule.makeTetherIngressKey());
|
||||||
|
} else {
|
||||||
|
verifyWithOrder(inOrder, mNetd).tetherOffloadRuleRemove(matches(rule));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyNeverTetherOffloadRuleRemove() throws Exception {
|
||||||
|
if (mDeps.isAtLeastS()) {
|
||||||
|
verify(mBpfIngressMap, never()).deleteEntry(any());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// late stage by manual cherry pick. It is risky if the R code flow has broken and be found at
|
||||||
|
// the last minute.
|
||||||
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
private void checkTetherOffloadRuleAdd(boolean usingApiS) throws Exception {
|
private void checkTetherOffloadRuleAddAndRemove(boolean usingApiS) throws Exception {
|
||||||
setupFunctioningNetdInterface();
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
// Replace Dependencies#isAtLeastS() for testing R and S+ BPF map apis. Note that |mDeps|
|
// Replace Dependencies#isAtLeastS() for testing R and S+ BPF map apis. Note that |mDeps|
|
||||||
@@ -241,18 +347,61 @@ public class BpfCoordinatorTest {
|
|||||||
final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
|
final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
|
||||||
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
||||||
verifyTetherOffloadRuleAdd(null, rule);
|
verifyTetherOffloadRuleAdd(null, rule);
|
||||||
|
|
||||||
|
// Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
|
||||||
|
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||||
|
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0));
|
||||||
|
coordinator.tetherOffloadRuleRemove(mIpServer, rule);
|
||||||
|
verifyTetherOffloadRuleRemove(null, rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
@Test
|
@Test
|
||||||
public void testTetherOffloadRuleAddSdkR() throws Exception {
|
public void testTetherOffloadRuleAddAndRemoveSdkR() throws Exception {
|
||||||
checkTetherOffloadRuleAdd(false /* R */);
|
checkTetherOffloadRuleAddAndRemove(false /* R */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
@Test
|
@Test
|
||||||
public void testTetherOffloadRuleAddAtLeastSdkS() throws Exception {
|
public void testTetherOffloadRuleAddAndRemoveAtLeastSdkS() throws Exception {
|
||||||
checkTetherOffloadRuleAdd(true /* S+ */);
|
checkTetherOffloadRuleAddAndRemove(true /* S+ */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
|
private void checkTetherOffloadGetStats(boolean usingApiS) throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
doReturn(usingApiS).when(mDeps).isAtLeastS();
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
coordinator.startPolling();
|
||||||
|
|
||||||
|
final String mobileIface = "rmnet_data0";
|
||||||
|
final Integer mobileIfIndex = 100;
|
||||||
|
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
|
||||||
|
|
||||||
|
updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] {
|
||||||
|
buildTestTetherStatsParcel(mobileIfIndex, 1000, 100, 2000, 200)});
|
||||||
|
|
||||||
|
final NetworkStats expectedIfaceStats = new NetworkStats(0L, 1)
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 1000, 100, 2000, 200));
|
||||||
|
|
||||||
|
final NetworkStats expectedUidStats = new NetworkStats(0L, 1)
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 1000, 100, 2000, 200));
|
||||||
|
|
||||||
|
mTetherStatsProvider.pushTetherStats();
|
||||||
|
mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
|
@Test
|
||||||
|
public void testTetherOffloadGetStatsSdkR() throws Exception {
|
||||||
|
checkTetherOffloadGetStats(false /* R */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove once presubmit tests on R even the code is submitted on S.
|
||||||
|
@Test
|
||||||
|
public void testTetherOffloadGetStatsAtLeastSdkS() throws Exception {
|
||||||
|
checkTetherOffloadGetStats(true /* S+ */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -275,7 +424,7 @@ public class BpfCoordinatorTest {
|
|||||||
// [1] Both interface stats are changed.
|
// [1] Both interface stats are changed.
|
||||||
// Setup the tether stats of wlan and mobile interface. Note that move forward the time of
|
// Setup the tether stats of wlan and mobile interface. Note that move forward the time of
|
||||||
// the looper to make sure the new tether stats has been updated by polling update thread.
|
// the looper to make sure the new tether stats has been updated by polling update thread.
|
||||||
setTetherOffloadStatsList(new TetherStatsParcel[] {
|
updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] {
|
||||||
buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
|
buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
|
||||||
buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)});
|
buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)});
|
||||||
|
|
||||||
@@ -296,7 +445,7 @@ public class BpfCoordinatorTest {
|
|||||||
// [2] Only one interface stats is changed.
|
// [2] Only one interface stats is changed.
|
||||||
// The tether stats of mobile interface is accumulated and The tether stats of wlan
|
// The tether stats of mobile interface is accumulated and The tether stats of wlan
|
||||||
// interface is the same.
|
// interface is the same.
|
||||||
setTetherOffloadStatsList(new TetherStatsParcel[] {
|
updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] {
|
||||||
buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
|
buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
|
||||||
buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)});
|
buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)});
|
||||||
|
|
||||||
@@ -317,12 +466,12 @@ public class BpfCoordinatorTest {
|
|||||||
// Shutdown the coordinator and clear the invocation history, especially the
|
// Shutdown the coordinator and clear the invocation history, especially the
|
||||||
// tetherOffloadGetStats() calls.
|
// tetherOffloadGetStats() calls.
|
||||||
coordinator.stopPolling();
|
coordinator.stopPolling();
|
||||||
clearInvocations(mNetd);
|
clearStatsInvocations();
|
||||||
|
|
||||||
// Verify the polling update thread stopped.
|
// Verify the polling update thread stopped.
|
||||||
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mNetd, never()).tetherOffloadGetStats();
|
verifyNeverTetherOffloadGetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -342,16 +491,14 @@ public class BpfCoordinatorTest {
|
|||||||
mTetherStatsProviderCb.expectNotifyAlertReached();
|
mTetherStatsProviderCb.expectNotifyAlertReached();
|
||||||
|
|
||||||
// Verify that notifyAlertReached never fired if quota is not yet reached.
|
// Verify that notifyAlertReached never fired if quota is not yet reached.
|
||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(
|
updateStatsEntry(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0));
|
||||||
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
|
|
||||||
mTetherStatsProvider.onSetAlert(100);
|
mTetherStatsProvider.onSetAlert(100);
|
||||||
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
mTetherStatsProviderCb.assertNoCallback();
|
mTetherStatsProviderCb.assertNoCallback();
|
||||||
|
|
||||||
// Verify that notifyAlertReached fired when quota is reached.
|
// Verify that notifyAlertReached fired when quota is reached.
|
||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(
|
updateStatsEntry(buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0));
|
||||||
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
|
|
||||||
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
mTetherStatsProviderCb.expectNotifyAlertReached();
|
mTetherStatsProviderCb.expectNotifyAlertReached();
|
||||||
@@ -510,14 +657,14 @@ public class BpfCoordinatorTest {
|
|||||||
|
|
||||||
// Removing the second rule on current upstream does not send the quota to netd.
|
// Removing the second rule on current upstream does not send the quota to netd.
|
||||||
coordinator.tetherOffloadRuleRemove(mIpServer, ruleB);
|
coordinator.tetherOffloadRuleRemove(mIpServer, ruleB);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB));
|
verifyTetherOffloadRuleRemove(inOrder, ruleB);
|
||||||
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||||
|
|
||||||
// Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
|
// Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
|
||||||
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||||
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0));
|
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0));
|
||||||
coordinator.tetherOffloadRuleRemove(mIpServer, ruleA);
|
coordinator.tetherOffloadRuleRemove(mIpServer, ruleA);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA));
|
verifyTetherOffloadRuleRemove(inOrder, ruleA);
|
||||||
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
||||||
inOrder.verifyNoMoreInteractions();
|
inOrder.verifyNoMoreInteractions();
|
||||||
}
|
}
|
||||||
@@ -569,10 +716,10 @@ public class BpfCoordinatorTest {
|
|||||||
// Update the existing rules for upstream changes. The rules are removed and re-added one
|
// Update the existing rules for upstream changes. The rules are removed and re-added one
|
||||||
// by one for updating upstream interface index by #tetherOffloadRuleUpdate.
|
// by one for updating upstream interface index by #tetherOffloadRuleUpdate.
|
||||||
coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
|
coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA));
|
verifyTetherOffloadRuleRemove(inOrder, ethernetRuleA);
|
||||||
verifyTetherOffloadRuleAdd(inOrder, mobileRuleA);
|
verifyTetherOffloadRuleAdd(inOrder, mobileRuleA);
|
||||||
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB));
|
verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB);
|
||||||
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex);
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex);
|
||||||
verifyTetherOffloadRuleAdd(inOrder, mobileRuleB);
|
verifyTetherOffloadRuleAdd(inOrder, mobileRuleB);
|
||||||
|
|
||||||
@@ -580,8 +727,8 @@ public class BpfCoordinatorTest {
|
|||||||
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||||
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80));
|
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80));
|
||||||
coordinator.tetherOffloadRuleClear(mIpServer);
|
coordinator.tetherOffloadRuleClear(mIpServer);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA));
|
verifyTetherOffloadRuleRemove(inOrder, mobileRuleA);
|
||||||
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB));
|
verifyTetherOffloadRuleRemove(inOrder, mobileRuleB);
|
||||||
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
||||||
|
|
||||||
// [4] Force pushing stats update to verify that the last diff of stats is reported on all
|
// [4] Force pushing stats update to verify that the last diff of stats is reported on all
|
||||||
@@ -606,7 +753,7 @@ public class BpfCoordinatorTest {
|
|||||||
// The tether stats polling task should not be scheduled.
|
// The tether stats polling task should not be scheduled.
|
||||||
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mNetd, never()).tetherOffloadGetStats();
|
verifyNeverTetherOffloadGetStats();
|
||||||
|
|
||||||
// The interface name lookup table can't be added.
|
// The interface name lookup table can't be added.
|
||||||
final String iface = "rmnet_data0";
|
final String iface = "rmnet_data0";
|
||||||
@@ -631,21 +778,21 @@ public class BpfCoordinatorTest {
|
|||||||
rules.put(rule.address, rule);
|
rules.put(rule.address, rule);
|
||||||
coordinator.getForwardingRulesForTesting().put(mIpServer, rules);
|
coordinator.getForwardingRulesForTesting().put(mIpServer, rules);
|
||||||
coordinator.tetherOffloadRuleRemove(mIpServer, rule);
|
coordinator.tetherOffloadRuleRemove(mIpServer, rule);
|
||||||
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
verifyNeverTetherOffloadRuleRemove();
|
||||||
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
assertNotNull(rules);
|
assertNotNull(rules);
|
||||||
assertEquals(1, rules.size());
|
assertEquals(1, rules.size());
|
||||||
|
|
||||||
// The rule can't be cleared.
|
// The rule can't be cleared.
|
||||||
coordinator.tetherOffloadRuleClear(mIpServer);
|
coordinator.tetherOffloadRuleClear(mIpServer);
|
||||||
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
verifyNeverTetherOffloadRuleRemove();
|
||||||
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
assertNotNull(rules);
|
assertNotNull(rules);
|
||||||
assertEquals(1, rules.size());
|
assertEquals(1, rules.size());
|
||||||
|
|
||||||
// The rule can't be updated.
|
// The rule can't be updated.
|
||||||
coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */);
|
coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */);
|
||||||
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
verifyNeverTetherOffloadRuleRemove();
|
||||||
verifyNeverTetherOffloadRuleAdd();
|
verifyNeverTetherOffloadRuleAdd();
|
||||||
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
assertNotNull(rules);
|
assertNotNull(rules);
|
||||||
@@ -669,6 +816,15 @@ public class BpfCoordinatorTest {
|
|||||||
checkBpfDisabled();
|
checkBpfDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.R)
|
||||||
|
public void testBpfDisabledbyNoBpfStatsMap() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
doReturn(null).when(mDeps).getBpfStatsMap();
|
||||||
|
|
||||||
|
checkBpfDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTetheringConfigSetPollingInterval() throws Exception {
|
public void testTetheringConfigSetPollingInterval() throws Exception {
|
||||||
setupFunctioningNetdInterface();
|
setupFunctioningNetdInterface();
|
||||||
@@ -703,18 +859,18 @@ public class BpfCoordinatorTest {
|
|||||||
// Start on a new polling time slot.
|
// Start on a new polling time slot.
|
||||||
mTestLooper.moveTimeForward(pollingInterval);
|
mTestLooper.moveTimeForward(pollingInterval);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
clearInvocations(mNetd);
|
clearStatsInvocations();
|
||||||
|
|
||||||
// Move time forward to 90% polling interval time. Expect that the polling thread has not
|
// Move time forward to 90% polling interval time. Expect that the polling thread has not
|
||||||
// scheduled yet.
|
// scheduled yet.
|
||||||
mTestLooper.moveTimeForward((long) (pollingInterval * 0.9));
|
mTestLooper.moveTimeForward((long) (pollingInterval * 0.9));
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mNetd, never()).tetherOffloadGetStats();
|
verifyNeverTetherOffloadGetStats();
|
||||||
|
|
||||||
// Move time forward to the remaining 10% polling interval time. Expect that the polling
|
// Move time forward to the remaining 10% polling interval time. Expect that the polling
|
||||||
// thread has scheduled.
|
// thread has scheduled.
|
||||||
mTestLooper.moveTimeForward((long) (pollingInterval * 0.1));
|
mTestLooper.moveTimeForward((long) (pollingInterval * 0.1));
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mNetd).tetherOffloadGetStats();
|
verifyTetherOffloadGetStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user