Make change and version bump to r_aml_300802000 for mainline module file: packages/Tethering/apex/manifest.json
Change-Id: I6601ad32346eec33bef7dde00ae7d744c86aa8e4
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name": "com.android.tethering",
|
"name": "com.android.tethering",
|
||||||
"version": 300801900
|
"version": 300802000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import static android.net.NetworkStats.UID_ALL;
|
|||||||
import static android.net.NetworkStats.UID_TETHERING;
|
import static android.net.NetworkStats.UID_TETHERING;
|
||||||
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
||||||
|
|
||||||
|
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
|
||||||
|
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
@@ -36,6 +38,7 @@ import android.net.ip.IpServer;
|
|||||||
import android.net.netstats.provider.NetworkStatsProvider;
|
import android.net.netstats.provider.NetworkStatsProvider;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.net.util.TetheringUtils.ForwardedStats;
|
import android.net.util.TetheringUtils.ForwardedStats;
|
||||||
|
import android.os.ConditionVariable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
@@ -47,11 +50,13 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
|
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,8 +70,7 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class BpfCoordinator {
|
public class BpfCoordinator {
|
||||||
private static final String TAG = BpfCoordinator.class.getSimpleName();
|
private static final String TAG = BpfCoordinator.class.getSimpleName();
|
||||||
@VisibleForTesting
|
private static final int DUMP_TIMEOUT_MS = 10_000;
|
||||||
static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable.
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
enum StatsType {
|
enum StatsType {
|
||||||
@@ -85,6 +89,13 @@ public class BpfCoordinator {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final BpfTetherStatsProvider mStatsProvider;
|
private final BpfTetherStatsProvider mStatsProvider;
|
||||||
|
|
||||||
|
// True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
|
||||||
|
// a runtime resource overlay package or device configuration. This flag is only initialized
|
||||||
|
// in the constructor because it is hard to unwind all existing change once device
|
||||||
|
// configuration is changed. Especially the forwarding rules. Keep the same setting
|
||||||
|
// to make it simpler. See also TetheringConfiguration.
|
||||||
|
private final boolean mIsBpfEnabled;
|
||||||
|
|
||||||
// Tracks whether BPF tethering is started or not. This is set by tethering before it
|
// Tracks whether BPF tethering is started or not. This is set by tethering before it
|
||||||
// starts the first IpServer and is cleared by tethering shortly before the last IpServer
|
// starts the first IpServer and is cleared by tethering shortly before the last IpServer
|
||||||
// is stopped. Note that rule updates (especially deletions, but sometimes additions as
|
// is stopped. Note that rule updates (especially deletions, but sometimes additions as
|
||||||
@@ -142,22 +153,34 @@ public class BpfCoordinator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static class Dependencies {
|
public abstract static class Dependencies {
|
||||||
int getPerformPollInterval() {
|
/** Get handler. */
|
||||||
// TODO: Consider make this configurable.
|
@NonNull public abstract Handler getHandler();
|
||||||
return DEFAULT_PERFORM_POLL_INTERVAL_MS;
|
|
||||||
}
|
/** Get netd. */
|
||||||
|
@NonNull public abstract INetd getNetd();
|
||||||
|
|
||||||
|
/** Get network stats manager. */
|
||||||
|
@NonNull public abstract NetworkStatsManager getNetworkStatsManager();
|
||||||
|
|
||||||
|
/** Get shared log. */
|
||||||
|
@NonNull public abstract SharedLog getSharedLog();
|
||||||
|
|
||||||
|
/** Get tethering configuration. */
|
||||||
|
@Nullable public abstract TetheringConfiguration getTetherConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd,
|
public BpfCoordinator(@NonNull Dependencies deps) {
|
||||||
@NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) {
|
mDeps = deps;
|
||||||
mHandler = handler;
|
mHandler = mDeps.getHandler();
|
||||||
mNetd = netd;
|
mNetd = mDeps.getNetd();
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = mDeps.getSharedLog().forSubComponent(TAG);
|
||||||
|
mIsBpfEnabled = isBpfEnabled();
|
||||||
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
|
||||||
try {
|
try {
|
||||||
nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
|
mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
|
||||||
|
getClass().getSimpleName(), provider);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
// TODO: Perhaps not allow to use BPF offload because the reregistration failure
|
// TODO: Perhaps not allow to use BPF offload because the reregistration failure
|
||||||
// implied that no data limit could be applies on a metered upstream if any.
|
// implied that no data limit could be applies on a metered upstream if any.
|
||||||
@@ -165,7 +188,6 @@ public class BpfCoordinator {
|
|||||||
provider = null;
|
provider = null;
|
||||||
}
|
}
|
||||||
mStatsProvider = provider;
|
mStatsProvider = provider;
|
||||||
mDeps = deps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,6 +199,11 @@ public class BpfCoordinator {
|
|||||||
public void startPolling() {
|
public void startPolling() {
|
||||||
if (mPollingStarted) return;
|
if (mPollingStarted) return;
|
||||||
|
|
||||||
|
if (!mIsBpfEnabled) {
|
||||||
|
mLog.i("Offload disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mPollingStarted = true;
|
mPollingStarted = true;
|
||||||
maybeSchedulePollingStats();
|
maybeSchedulePollingStats();
|
||||||
|
|
||||||
@@ -211,6 +238,8 @@ public class BpfCoordinator {
|
|||||||
*/
|
*/
|
||||||
public void tetherOffloadRuleAdd(
|
public void tetherOffloadRuleAdd(
|
||||||
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
||||||
|
if (!mIsBpfEnabled) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: Perhaps avoid to add a duplicate rule.
|
// TODO: Perhaps avoid to add a duplicate rule.
|
||||||
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
|
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
|
||||||
@@ -250,6 +279,8 @@ public class BpfCoordinator {
|
|||||||
*/
|
*/
|
||||||
public void tetherOffloadRuleRemove(
|
public void tetherOffloadRuleRemove(
|
||||||
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
@NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
|
||||||
|
if (!mIsBpfEnabled) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: Perhaps avoid to remove a non-existent rule.
|
// TODO: Perhaps avoid to remove a non-existent rule.
|
||||||
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
|
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
|
||||||
@@ -293,6 +324,8 @@ public class BpfCoordinator {
|
|||||||
* Note that this can be only called on handler thread.
|
* Note that this can be only called on handler thread.
|
||||||
*/
|
*/
|
||||||
public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
|
public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
|
||||||
|
if (!mIsBpfEnabled) return;
|
||||||
|
|
||||||
final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
|
final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
|
||||||
ipServer);
|
ipServer);
|
||||||
if (rules == null) return;
|
if (rules == null) return;
|
||||||
@@ -308,6 +341,8 @@ public class BpfCoordinator {
|
|||||||
* Note that this can be only called on handler thread.
|
* Note that this can be only called on handler thread.
|
||||||
*/
|
*/
|
||||||
public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
|
public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
|
||||||
|
if (!mIsBpfEnabled) return;
|
||||||
|
|
||||||
final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
|
final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
|
||||||
ipServer);
|
ipServer);
|
||||||
if (rules == null) return;
|
if (rules == null) return;
|
||||||
@@ -330,6 +365,8 @@ public class BpfCoordinator {
|
|||||||
* Note that this can be only called on handler thread.
|
* Note that this can be only called on handler thread.
|
||||||
*/
|
*/
|
||||||
public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
|
public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
|
||||||
|
if (!mIsBpfEnabled) return;
|
||||||
|
|
||||||
if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
|
if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
|
||||||
|
|
||||||
// The same interface index to name mapping may be added by different IpServer objects or
|
// The same interface index to name mapping may be added by different IpServer objects or
|
||||||
@@ -344,6 +381,77 @@ public class BpfCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump information.
|
||||||
|
* Block the function until all the data are dumped on the handler thread or timed-out. The
|
||||||
|
* reason is that dumpsys invokes this function on the thread of caller and the data may only
|
||||||
|
* be allowed to be accessed on the handler thread.
|
||||||
|
*/
|
||||||
|
public void dump(@NonNull IndentingPrintWriter pw) {
|
||||||
|
final ConditionVariable dumpDone = new ConditionVariable();
|
||||||
|
mHandler.post(() -> {
|
||||||
|
pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
|
||||||
|
pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
|
||||||
|
pw.println("Stats provider " + (mStatsProvider != null
|
||||||
|
? "registered" : "not registered"));
|
||||||
|
pw.println("Upstream quota: " + mInterfaceQuotas.toString());
|
||||||
|
pw.println("Polling interval: " + getPollingInterval() + " ms");
|
||||||
|
|
||||||
|
pw.println("Forwarding stats:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
if (mStats.size() == 0) {
|
||||||
|
pw.println("<empty>");
|
||||||
|
} else {
|
||||||
|
dumpStats(pw);
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("Forwarding rules:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
if (mIpv6ForwardingRules.size() == 0) {
|
||||||
|
pw.println("<empty>");
|
||||||
|
} else {
|
||||||
|
dumpIpv6ForwardingRules(pw);
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
dumpDone.open();
|
||||||
|
});
|
||||||
|
if (!dumpDone.block(DUMP_TIMEOUT_MS)) {
|
||||||
|
pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dumpStats(@NonNull IndentingPrintWriter pw) {
|
||||||
|
for (int i = 0; i < mStats.size(); i++) {
|
||||||
|
final int upstreamIfindex = mStats.keyAt(i);
|
||||||
|
final ForwardedStats stats = mStats.get(upstreamIfindex);
|
||||||
|
pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get(
|
||||||
|
upstreamIfindex), stats.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) {
|
||||||
|
for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry :
|
||||||
|
mIpv6ForwardingRules.entrySet()) {
|
||||||
|
IpServer ipServer = entry.getKey();
|
||||||
|
// The rule downstream interface index is paired with the interface name from
|
||||||
|
// IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer.
|
||||||
|
final String downstreamIface = ipServer.interfaceName();
|
||||||
|
pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac");
|
||||||
|
|
||||||
|
pw.increaseIndent();
|
||||||
|
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue();
|
||||||
|
for (Ipv6ForwardingRule rule : rules.values()) {
|
||||||
|
final int upstreamIfindex = rule.upstreamIfindex;
|
||||||
|
pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex,
|
||||||
|
mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex,
|
||||||
|
downstreamIface, rule.address, rule.srcMac, rule.dstMac));
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** IPv6 forwarding rule class. */
|
/** IPv6 forwarding rule class. */
|
||||||
public static class Ipv6ForwardingRule {
|
public static class Ipv6ForwardingRule {
|
||||||
public final int upstreamIfindex;
|
public final int upstreamIfindex;
|
||||||
@@ -474,6 +582,11 @@ public class BpfCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBpfEnabled() {
|
||||||
|
final TetheringConfiguration config = mDeps.getTetherConfig();
|
||||||
|
return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
|
||||||
|
}
|
||||||
|
|
||||||
private int getInterfaceIndexFromRules(@NonNull String ifName) {
|
private int getInterfaceIndexFromRules(@NonNull String ifName) {
|
||||||
for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
|
for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
|
||||||
.values()) {
|
.values()) {
|
||||||
@@ -625,6 +738,17 @@ public class BpfCoordinator {
|
|||||||
updateQuotaAndStatsFromSnapshot(tetherStatsList);
|
updateQuotaAndStatsFromSnapshot(tetherStatsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
int getPollingInterval() {
|
||||||
|
// The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
|
||||||
|
// Ignore the config value is less than the minimum polling interval. Note that the
|
||||||
|
// minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does.
|
||||||
|
// TODO: Perhaps define a minimum polling interval constant.
|
||||||
|
final TetheringConfiguration config = mDeps.getTetherConfig();
|
||||||
|
final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0;
|
||||||
|
return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
|
||||||
|
}
|
||||||
|
|
||||||
private void maybeSchedulePollingStats() {
|
private void maybeSchedulePollingStats() {
|
||||||
if (!mPollingStarted) return;
|
if (!mPollingStarted) return;
|
||||||
|
|
||||||
@@ -632,6 +756,23 @@ public class BpfCoordinator {
|
|||||||
mHandler.removeCallbacks(mScheduledPollingTask);
|
mHandler.removeCallbacks(mScheduledPollingTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval());
|
mHandler.postDelayed(mScheduledPollingTask, getPollingInterval());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return forwarding rule map. This is used for testing only.
|
||||||
|
// Note that this can be only called on handler thread.
|
||||||
|
@NonNull
|
||||||
|
@VisibleForTesting
|
||||||
|
final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
|
||||||
|
getForwardingRulesForTesting() {
|
||||||
|
return mIpv6ForwardingRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return upstream interface name map. This is used for testing only.
|
||||||
|
// Note that this can be only called on handler thread.
|
||||||
|
@NonNull
|
||||||
|
@VisibleForTesting
|
||||||
|
final SparseArray<String> getInterfaceNamesForTesting() {
|
||||||
|
return mInterfaceNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|||||||
|
|
||||||
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
|
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
|
||||||
|
|
||||||
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothPan;
|
import android.bluetooth.BluetoothPan;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
@@ -286,8 +287,6 @@ public class Tethering {
|
|||||||
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
|
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
|
||||||
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
|
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
|
||||||
mForwardedDownstreams = new LinkedHashSet<>();
|
mForwardedDownstreams = new LinkedHashSet<>();
|
||||||
mBpfCoordinator = mDeps.getBpfCoordinator(
|
|
||||||
mHandler, mNetd, mLog, new BpfCoordinator.Dependencies());
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
|
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
|
||||||
@@ -325,6 +324,36 @@ public class Tethering {
|
|||||||
// Load tethering configuration.
|
// Load tethering configuration.
|
||||||
updateConfiguration();
|
updateConfiguration();
|
||||||
|
|
||||||
|
// Must be initialized after tethering configuration is loaded because BpfCoordinator
|
||||||
|
// constructor needs to use the configuration.
|
||||||
|
mBpfCoordinator = mDeps.getBpfCoordinator(
|
||||||
|
new BpfCoordinator.Dependencies() {
|
||||||
|
@NonNull
|
||||||
|
public Handler getHandler() {
|
||||||
|
return mHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public INetd getNetd() {
|
||||||
|
return mNetd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public NetworkStatsManager getNetworkStatsManager() {
|
||||||
|
return mContext.getSystemService(NetworkStatsManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public SharedLog getSharedLog() {
|
||||||
|
return mLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public TetheringConfiguration getTetherConfig() {
|
||||||
|
return mConfig;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
startStateMachineUpdaters();
|
startStateMachineUpdaters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2236,6 +2265,11 @@ public class Tethering {
|
|||||||
mOffloadController.dump(pw);
|
mOffloadController.dump(pw);
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("BPF offload:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
mBpfCoordinator.dump(pw);
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
pw.println("Private address coordinator:");
|
pw.println("Private address coordinator:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
mPrivateAddressCoordinator.dump(pw);
|
mPrivateAddressCoordinator.dump(pw);
|
||||||
@@ -2370,7 +2404,7 @@ public class Tethering {
|
|||||||
final TetherState tetherState = new TetherState(
|
final TetherState tetherState = new TetherState(
|
||||||
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
|
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
|
||||||
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
||||||
mConfig.enableBpfOffload, mPrivateAddressCoordinator,
|
mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator,
|
||||||
mDeps.getIpServerDependencies()));
|
mDeps.getIpServerDependencies()));
|
||||||
mTetherStates.put(iface, tetherState);
|
mTetherStates.put(iface, tetherState);
|
||||||
tetherState.ipServer.start();
|
tetherState.ipServer.start();
|
||||||
|
|||||||
@@ -101,8 +101,6 @@ public class TetheringConfiguration {
|
|||||||
public final String[] legacyDhcpRanges;
|
public final String[] legacyDhcpRanges;
|
||||||
public final String[] defaultIPv4DNS;
|
public final String[] defaultIPv4DNS;
|
||||||
public final boolean enableLegacyDhcpServer;
|
public final boolean enableLegacyDhcpServer;
|
||||||
// TODO: Add to TetheringConfigurationParcel if required.
|
|
||||||
public final boolean enableBpfOffload;
|
|
||||||
|
|
||||||
public final String[] provisioningApp;
|
public final String[] provisioningApp;
|
||||||
public final String provisioningAppNoUi;
|
public final String provisioningAppNoUi;
|
||||||
@@ -112,6 +110,8 @@ public class TetheringConfiguration {
|
|||||||
public final int activeDataSubId;
|
public final int activeDataSubId;
|
||||||
|
|
||||||
private final int mOffloadPollInterval;
|
private final int mOffloadPollInterval;
|
||||||
|
// TODO: Add to TetheringConfigurationParcel if required.
|
||||||
|
private final boolean mEnableBpfOffload;
|
||||||
|
|
||||||
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
|
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
|
||||||
final SharedLog configLog = log.forSubComponent("config");
|
final SharedLog configLog = log.forSubComponent("config");
|
||||||
@@ -138,7 +138,7 @@ public class TetheringConfiguration {
|
|||||||
|
|
||||||
legacyDhcpRanges = getLegacyDhcpRanges(res);
|
legacyDhcpRanges = getLegacyDhcpRanges(res);
|
||||||
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
|
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
|
||||||
enableBpfOffload = getEnableBpfOffload(res);
|
mEnableBpfOffload = getEnableBpfOffload(res);
|
||||||
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
|
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
|
||||||
|
|
||||||
provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
|
provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
|
||||||
@@ -222,7 +222,7 @@ public class TetheringConfiguration {
|
|||||||
pw.println(provisioningAppNoUi);
|
pw.println(provisioningAppNoUi);
|
||||||
|
|
||||||
pw.print("enableBpfOffload: ");
|
pw.print("enableBpfOffload: ");
|
||||||
pw.println(enableBpfOffload);
|
pw.println(mEnableBpfOffload);
|
||||||
|
|
||||||
pw.print("enableLegacyDhcpServer: ");
|
pw.print("enableLegacyDhcpServer: ");
|
||||||
pw.println(enableLegacyDhcpServer);
|
pw.println(enableLegacyDhcpServer);
|
||||||
@@ -244,7 +244,7 @@ public class TetheringConfiguration {
|
|||||||
toIntArray(preferredUpstreamIfaceTypes)));
|
toIntArray(preferredUpstreamIfaceTypes)));
|
||||||
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
|
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
|
||||||
sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
|
sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
|
||||||
sj.add(String.format("enableBpfOffload:%s", enableBpfOffload));
|
sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
|
||||||
sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
|
sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
|
||||||
return String.format("TetheringConfiguration{%s}", sj.toString());
|
return String.format("TetheringConfiguration{%s}", sj.toString());
|
||||||
}
|
}
|
||||||
@@ -283,6 +283,10 @@ public class TetheringConfiguration {
|
|||||||
return mOffloadPollInterval;
|
return mOffloadPollInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBpfOffloadEnabled() {
|
||||||
|
return mEnableBpfOffload;
|
||||||
|
}
|
||||||
|
|
||||||
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
|
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
|
||||||
final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
|
final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
|
||||||
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
|
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
|
||||||
|
|||||||
@@ -46,11 +46,8 @@ public abstract class TetheringDependencies {
|
|||||||
* Get a reference to the BpfCoordinator to be used by tethering.
|
* Get a reference to the BpfCoordinator to be used by tethering.
|
||||||
*/
|
*/
|
||||||
public @NonNull BpfCoordinator getBpfCoordinator(
|
public @NonNull BpfCoordinator getBpfCoordinator(
|
||||||
@NonNull Handler handler, @NonNull INetd netd, @NonNull SharedLog log,
|
|
||||||
@NonNull BpfCoordinator.Dependencies deps) {
|
@NonNull BpfCoordinator.Dependencies deps) {
|
||||||
final NetworkStatsManager statsManager =
|
return new BpfCoordinator(deps);
|
||||||
(NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
|
|
||||||
return new BpfCoordinator(handler, netd, statsManager, log, deps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -89,12 +89,14 @@ import android.os.test.TestLooper;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.networkstack.tethering.BpfCoordinator;
|
import com.android.networkstack.tethering.BpfCoordinator;
|
||||||
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||||
|
import com.android.networkstack.tethering.TetheringConfiguration;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -142,6 +144,7 @@ public class IpServerTest {
|
|||||||
@Mock private IpServer.Dependencies mDependencies;
|
@Mock private IpServer.Dependencies mDependencies;
|
||||||
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||||
|
|
||||||
@@ -225,10 +228,35 @@ public class IpServerTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
|
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
|
||||||
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
|
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
|
||||||
|
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
|
||||||
|
|
||||||
BpfCoordinator bc = new BpfCoordinator(new Handler(mLooper.getLooper()), mNetd,
|
mBpfCoordinator = spy(new BpfCoordinator(
|
||||||
mStatsManager, mSharedLog, new BpfCoordinator.Dependencies());
|
new BpfCoordinator.Dependencies() {
|
||||||
mBpfCoordinator = spy(bc);
|
@NonNull
|
||||||
|
public Handler getHandler() {
|
||||||
|
return new Handler(mLooper.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public INetd getNetd() {
|
||||||
|
return mNetd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public NetworkStatsManager getNetworkStatsManager() {
|
||||||
|
return mStatsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public SharedLog getSharedLog() {
|
||||||
|
return mSharedLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public TetheringConfiguration getTetherConfig() {
|
||||||
|
return mTetherConfig;
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -671,18 +699,21 @@ public class IpServerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TetherOffloadRuleParcel matches(
|
@NonNull
|
||||||
|
private static TetherOffloadRuleParcel matches(
|
||||||
int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
|
int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
|
||||||
return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
|
return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
private static Ipv6ForwardingRule makeForwardingRule(
|
private static Ipv6ForwardingRule makeForwardingRule(
|
||||||
int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) {
|
int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) {
|
||||||
return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index,
|
return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index,
|
||||||
(Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac);
|
(Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
|
@NonNull
|
||||||
|
private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
|
||||||
TetherStatsParcel parcel = new TetherStatsParcel();
|
TetherStatsParcel parcel = new TetherStatsParcel();
|
||||||
parcel.ifIndex = ifIndex;
|
parcel.ifIndex = ifIndex;
|
||||||
return parcel;
|
return parcel;
|
||||||
|
|||||||
@@ -25,48 +25,76 @@ import static android.net.NetworkStats.UID_ALL;
|
|||||||
import static android.net.NetworkStats.UID_TETHERING;
|
import static android.net.NetworkStats.UID_TETHERING;
|
||||||
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
||||||
|
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator
|
|
||||||
.DEFAULT_PERFORM_POLL_INTERVAL_MS;
|
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
|
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
|
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
|
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
|
||||||
|
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
import static org.mockito.Matchers.anyLong;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.argThat;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
|
import android.net.InetAddresses;
|
||||||
|
import android.net.MacAddress;
|
||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
|
import android.net.TetherOffloadRuleParcel;
|
||||||
import android.net.TetherStatsParcel;
|
import android.net.TetherStatsParcel;
|
||||||
|
import android.net.ip.IpServer;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
|
||||||
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
|
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.net.Inet6Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public class BpfCoordinatorTest {
|
public class BpfCoordinatorTest {
|
||||||
|
private static final int DOWNSTREAM_IFINDEX = 10;
|
||||||
|
private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS;
|
||||||
|
private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
|
||||||
|
private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2");
|
||||||
|
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");
|
||||||
|
|
||||||
@Mock private NetworkStatsManager mStatsManager;
|
@Mock private NetworkStatsManager mStatsManager;
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
|
@Mock private IpServer mIpServer;
|
||||||
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
|
|
||||||
// Late init since methods must be called by the thread that created this object.
|
// Late init since methods must be called by the thread that created this object.
|
||||||
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
|
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
|
||||||
private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
|
private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
|
||||||
@@ -75,14 +103,35 @@ public class BpfCoordinatorTest {
|
|||||||
private final TestLooper mTestLooper = new TestLooper();
|
private final TestLooper mTestLooper = new TestLooper();
|
||||||
private BpfCoordinator.Dependencies mDeps =
|
private BpfCoordinator.Dependencies mDeps =
|
||||||
new BpfCoordinator.Dependencies() {
|
new BpfCoordinator.Dependencies() {
|
||||||
@Override
|
@NonNull
|
||||||
int getPerformPollInterval() {
|
public Handler getHandler() {
|
||||||
return DEFAULT_PERFORM_POLL_INTERVAL_MS;
|
return new Handler(mTestLooper.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public INetd getNetd() {
|
||||||
|
return mNetd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public NetworkStatsManager getNetworkStatsManager() {
|
||||||
|
return mStatsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public SharedLog getSharedLog() {
|
||||||
|
return new SharedLog("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public TetheringConfiguration getTetherConfig() {
|
||||||
|
return mTetherConfig;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Before public void setUp() {
|
@Before public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForIdle() {
|
private void waitForIdle() {
|
||||||
@@ -95,9 +144,7 @@ public class BpfCoordinatorTest {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private BpfCoordinator makeBpfCoordinator() throws Exception {
|
private BpfCoordinator makeBpfCoordinator() throws Exception {
|
||||||
BpfCoordinator coordinator = new BpfCoordinator(
|
final BpfCoordinator coordinator = new BpfCoordinator(mDeps);
|
||||||
new Handler(mTestLooper.getLooper()), mNetd, mStatsManager, new SharedLog("test"),
|
|
||||||
mDeps);
|
|
||||||
final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
|
final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
|
||||||
tetherStatsProviderCaptor =
|
tetherStatsProviderCaptor =
|
||||||
ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
|
ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
|
||||||
@@ -130,9 +177,11 @@ public class BpfCoordinatorTest {
|
|||||||
return parcel;
|
return parcel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up 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.
|
||||||
private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
|
private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
|
||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
|
when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
|
||||||
mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +250,7 @@ public class BpfCoordinatorTest {
|
|||||||
clearInvocations(mNetd);
|
clearInvocations(mNetd);
|
||||||
|
|
||||||
// Verify the polling update thread stopped.
|
// Verify the polling update thread stopped.
|
||||||
mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mNetd, never()).tetherOffloadGetStats();
|
verify(mNetd, never()).tetherOffloadGetStats();
|
||||||
}
|
}
|
||||||
@@ -226,21 +275,333 @@ public class BpfCoordinatorTest {
|
|||||||
when(mNetd.tetherOffloadGetStats()).thenReturn(
|
when(mNetd.tetherOffloadGetStats()).thenReturn(
|
||||||
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
|
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
|
||||||
mTetherStatsProvider.onSetAlert(100);
|
mTetherStatsProvider.onSetAlert(100);
|
||||||
mTestLooper.moveTimeForward(DEFAULT_PERFORM_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(
|
when(mNetd.tetherOffloadGetStats()).thenReturn(
|
||||||
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
|
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
|
||||||
mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
mTetherStatsProviderCb.expectNotifyAlertReached();
|
mTetherStatsProviderCb.expectNotifyAlertReached();
|
||||||
|
|
||||||
// Verify that set quota with UNLIMITED won't trigger any callback.
|
// Verify that set quota with UNLIMITED won't trigger any callback.
|
||||||
mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED);
|
mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED);
|
||||||
mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
mTetherStatsProviderCb.assertNoCallback();
|
mTetherStatsProviderCb.assertNoCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The custom ArgumentMatcher simply comes from IpServerTest.
|
||||||
|
// TODO: move both of them into a common utility class for reusing the code.
|
||||||
|
private static class TetherOffloadRuleParcelMatcher implements
|
||||||
|
ArgumentMatcher<TetherOffloadRuleParcel> {
|
||||||
|
public final int upstreamIfindex;
|
||||||
|
public final int downstreamIfindex;
|
||||||
|
public final Inet6Address address;
|
||||||
|
public final MacAddress srcMac;
|
||||||
|
public final MacAddress dstMac;
|
||||||
|
|
||||||
|
TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) {
|
||||||
|
upstreamIfindex = rule.upstreamIfindex;
|
||||||
|
downstreamIfindex = rule.downstreamIfindex;
|
||||||
|
address = rule.address;
|
||||||
|
srcMac = rule.srcMac;
|
||||||
|
dstMac = rule.dstMac;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(@NonNull TetherOffloadRuleParcel parcel) {
|
||||||
|
return upstreamIfindex == parcel.inputInterfaceIndex
|
||||||
|
&& (downstreamIfindex == parcel.outputInterfaceIndex)
|
||||||
|
&& Arrays.equals(address.getAddress(), parcel.destination)
|
||||||
|
&& (128 == parcel.prefixLength)
|
||||||
|
&& Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address)
|
||||||
|
&& Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s",
|
||||||
|
upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) {
|
||||||
|
return argThat(new TetherOffloadRuleParcelMatcher(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Ipv6ForwardingRule buildTestForwardingRule(
|
||||||
|
int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) {
|
||||||
|
return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address,
|
||||||
|
DOWNSTREAM_MAC, dstMac);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetDataLimit() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
final String mobileIface = "rmnet_data0";
|
||||||
|
final Integer mobileIfIndex = 100;
|
||||||
|
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
|
||||||
|
|
||||||
|
// [1] Default limit.
|
||||||
|
// 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);
|
||||||
|
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(rule));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
|
||||||
|
// [2] Specific limit.
|
||||||
|
// Applying the data limit boundary {min, 1gb, max, infinity} on current upstream.
|
||||||
|
for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) {
|
||||||
|
mTetherStatsProvider.onSetLimit(mobileIface, quota);
|
||||||
|
waitForIdle();
|
||||||
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota);
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
}
|
||||||
|
|
||||||
|
// [3] Invalid limit.
|
||||||
|
// The valid range of quota is 0..max_int64 or -1 (unlimited).
|
||||||
|
final long invalidLimit = Long.MIN_VALUE;
|
||||||
|
try {
|
||||||
|
mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit);
|
||||||
|
waitForIdle();
|
||||||
|
fail("No exception thrown for invalid limit " + invalidLimit + ".");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test the case in which the rules are changed from different IpServer objects.
|
||||||
|
@Test
|
||||||
|
public void testSetDataLimitOnRuleChange() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
final String mobileIface = "rmnet_data0";
|
||||||
|
final Integer mobileIfIndex = 100;
|
||||||
|
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
mTetherStatsProvider.onSetLimit(mobileIface, limit);
|
||||||
|
waitForIdle();
|
||||||
|
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit);
|
||||||
|
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);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleB));
|
||||||
|
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||||
|
|
||||||
|
// Removing the second rule on current upstream does not send the quota to netd.
|
||||||
|
coordinator.tetherOffloadRuleRemove(mIpServer, ruleB);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB));
|
||||||
|
inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
|
||||||
|
|
||||||
|
// 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, ruleA);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTetherOffloadRuleUpdateAndClear() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
final String ethIface = "eth1";
|
||||||
|
final String mobileIface = "rmnet_data0";
|
||||||
|
final Integer ethIfIndex = 100;
|
||||||
|
final Integer mobileIfIndex = 101;
|
||||||
|
coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface);
|
||||||
|
coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
|
||||||
|
|
||||||
|
final InOrder inOrder = inOrder(mNetd);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// If the service has never applied the data limit, set an unlimited quota as default.
|
||||||
|
// - After removing the last rule on a given upstream, the coordinator gets the last stats.
|
||||||
|
// Then, it clears the stats and the limit entry from BPF maps.
|
||||||
|
// See tetherOffloadRule{Add, Remove, Clear, Clean}.
|
||||||
|
|
||||||
|
// [1] Adding rules on the upstream Ethernet.
|
||||||
|
// Note that the default data limit is applied after the first rule is added.
|
||||||
|
final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule(
|
||||||
|
ethIfIndex, NEIGH_A, MAC_A);
|
||||||
|
final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule(
|
||||||
|
ethIfIndex, NEIGH_B, MAC_B);
|
||||||
|
|
||||||
|
coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED);
|
||||||
|
|
||||||
|
coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleB));
|
||||||
|
|
||||||
|
// [2] Update the existing rules from Ethernet to cellular.
|
||||||
|
final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule(
|
||||||
|
mobileIfIndex, NEIGH_A, MAC_A);
|
||||||
|
final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule(
|
||||||
|
mobileIfIndex, NEIGH_B, MAC_B);
|
||||||
|
when(mNetd.tetherOffloadGetAndClearStats(ethIfIndex))
|
||||||
|
.thenReturn(buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40));
|
||||||
|
|
||||||
|
// Update the existing rules for upstream changes. The rules are removed and re-added one
|
||||||
|
// by one for updating upstream interface index by #tetherOffloadRuleUpdate.
|
||||||
|
coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleB));
|
||||||
|
|
||||||
|
// [3] Clear all rules for a given IpServer.
|
||||||
|
when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
|
||||||
|
.thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80));
|
||||||
|
coordinator.tetherOffloadRuleClear(mIpServer);
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB));
|
||||||
|
inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
|
||||||
|
|
||||||
|
// [4] Force pushing stats update to verify that the last diff of stats is reported on all
|
||||||
|
// upstreams.
|
||||||
|
mTetherStatsProvider.pushTetherStats();
|
||||||
|
mTetherStatsProviderCb.expectNotifyStatsUpdated(
|
||||||
|
new NetworkStats(0L, 2)
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40))
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)),
|
||||||
|
new NetworkStats(0L, 2)
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40))
|
||||||
|
.addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTetheringConfigDisable() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false);
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
coordinator.startPolling();
|
||||||
|
|
||||||
|
// The tether stats polling task should not be scheduled.
|
||||||
|
mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||||
|
waitForIdle();
|
||||||
|
verify(mNetd, never()).tetherOffloadGetStats();
|
||||||
|
|
||||||
|
// The interface name lookup table can't be added.
|
||||||
|
final String iface = "rmnet_data0";
|
||||||
|
final Integer ifIndex = 100;
|
||||||
|
coordinator.addUpstreamNameToLookupTable(ifIndex, iface);
|
||||||
|
assertEquals(0, coordinator.getInterfaceNamesForTesting().size());
|
||||||
|
|
||||||
|
// The rule can't be added.
|
||||||
|
final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
|
||||||
|
final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a");
|
||||||
|
final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac);
|
||||||
|
coordinator.tetherOffloadRuleAdd(mIpServer, rule);
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleAdd(any());
|
||||||
|
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules =
|
||||||
|
coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
|
assertNull(rules);
|
||||||
|
|
||||||
|
// The rule can't be removed. This is not a realistic case because adding rule is not
|
||||||
|
// allowed. That implies no rule could be removed, cleared or updated. Verify these
|
||||||
|
// cases just in case.
|
||||||
|
rules = new LinkedHashMap<Inet6Address, Ipv6ForwardingRule>();
|
||||||
|
rules.put(rule.address, rule);
|
||||||
|
coordinator.getForwardingRulesForTesting().put(mIpServer, rules);
|
||||||
|
coordinator.tetherOffloadRuleRemove(mIpServer, rule);
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
||||||
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
|
assertNotNull(rules);
|
||||||
|
assertEquals(1, rules.size());
|
||||||
|
|
||||||
|
// The rule can't be cleared.
|
||||||
|
coordinator.tetherOffloadRuleClear(mIpServer);
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
||||||
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
|
assertNotNull(rules);
|
||||||
|
assertEquals(1, rules.size());
|
||||||
|
|
||||||
|
// The rule can't be updated.
|
||||||
|
coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */);
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleRemove(any());
|
||||||
|
verify(mNetd, never()).tetherOffloadRuleAdd(any());
|
||||||
|
rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
|
||||||
|
assertNotNull(rules);
|
||||||
|
assertEquals(1, rules.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTetheringConfigSetPollingInterval() throws Exception {
|
||||||
|
setupFunctioningNetdInterface();
|
||||||
|
|
||||||
|
final BpfCoordinator coordinator = makeBpfCoordinator();
|
||||||
|
|
||||||
|
// [1] The default polling interval.
|
||||||
|
coordinator.startPolling();
|
||||||
|
assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval());
|
||||||
|
coordinator.stopPolling();
|
||||||
|
|
||||||
|
// [2] Expect the invalid polling interval isn't applied. The valid range of interval is
|
||||||
|
// DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
|
||||||
|
for (final int interval
|
||||||
|
: new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) {
|
||||||
|
when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval);
|
||||||
|
coordinator.startPolling();
|
||||||
|
assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval());
|
||||||
|
coordinator.stopPolling();
|
||||||
|
}
|
||||||
|
|
||||||
|
// [3] Set a specific polling interval which is larger than default value.
|
||||||
|
// Use a large polling interval to avoid flaky test because the time forwarding
|
||||||
|
// approximation is used to verify the scheduled time of the polling thread.
|
||||||
|
final int pollingInterval = 100_000;
|
||||||
|
when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval);
|
||||||
|
coordinator.startPolling();
|
||||||
|
|
||||||
|
// Expect the specific polling interval to be applied.
|
||||||
|
assertEquals(pollingInterval, coordinator.getPollingInterval());
|
||||||
|
|
||||||
|
// Start on a new polling time slot.
|
||||||
|
mTestLooper.moveTimeForward(pollingInterval);
|
||||||
|
waitForIdle();
|
||||||
|
clearInvocations(mNetd);
|
||||||
|
|
||||||
|
// Move time forward to 90% polling interval time. Expect that the polling thread has not
|
||||||
|
// scheduled yet.
|
||||||
|
mTestLooper.moveTimeForward((long) (pollingInterval * 0.9));
|
||||||
|
waitForIdle();
|
||||||
|
verify(mNetd, never()).tetherOffloadGetStats();
|
||||||
|
|
||||||
|
// Move time forward to the remaining 10% polling interval time. Expect that the polling
|
||||||
|
// thread has scheduled.
|
||||||
|
mTestLooper.moveTimeForward((long) (pollingInterval * 0.1));
|
||||||
|
waitForIdle();
|
||||||
|
verify(mNetd).tetherOffloadGetStats();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ public class TetheringConfigurationTest {
|
|||||||
initializeBpfOffloadConfiguration(true, null /* unset */);
|
initializeBpfOffloadConfiguration(true, null /* unset */);
|
||||||
final TetheringConfiguration enableByRes =
|
final TetheringConfiguration enableByRes =
|
||||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||||
assertTrue(enableByRes.enableBpfOffload);
|
assertTrue(enableByRes.isBpfOffloadEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -303,7 +303,7 @@ public class TetheringConfigurationTest {
|
|||||||
initializeBpfOffloadConfiguration(res, "true");
|
initializeBpfOffloadConfiguration(res, "true");
|
||||||
final TetheringConfiguration enableByDevConOverride =
|
final TetheringConfiguration enableByDevConOverride =
|
||||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||||
assertTrue(enableByDevConOverride.enableBpfOffload);
|
assertTrue(enableByDevConOverride.isBpfOffloadEnabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ public class TetheringConfigurationTest {
|
|||||||
initializeBpfOffloadConfiguration(false, null /* unset */);
|
initializeBpfOffloadConfiguration(false, null /* unset */);
|
||||||
final TetheringConfiguration disableByRes =
|
final TetheringConfiguration disableByRes =
|
||||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||||
assertFalse(disableByRes.enableBpfOffload);
|
assertFalse(disableByRes.isBpfOffloadEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -321,7 +321,7 @@ public class TetheringConfigurationTest {
|
|||||||
initializeBpfOffloadConfiguration(res, "false");
|
initializeBpfOffloadConfiguration(res, "false");
|
||||||
final TetheringConfiguration disableByDevConOverride =
|
final TetheringConfiguration disableByDevConOverride =
|
||||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||||
assertFalse(disableByDevConOverride.enableBpfOffload);
|
assertFalse(disableByDevConOverride.isBpfOffloadEnabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -346,8 +346,8 @@ public class TetheringTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BpfCoordinator getBpfCoordinator(Handler handler, INetd netd,
|
public BpfCoordinator getBpfCoordinator(
|
||||||
SharedLog log, BpfCoordinator.Dependencies deps) {
|
BpfCoordinator.Dependencies deps) {
|
||||||
return mBpfCoordinator;
|
return mBpfCoordinator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user