Merge "[BOT.13] Make offload coordinator poll interval configurable" into rvc-dev
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 12464e1..20f30ea 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -25,6 +25,8 @@
import static android.net.NetworkStats.UID_TETHERING;
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.net.INetd;
import android.net.MacAddress;
@@ -69,8 +71,6 @@
public class BpfCoordinator {
private static final String TAG = BpfCoordinator.class.getSimpleName();
private static final int DUMP_TIMEOUT_MS = 10_000;
- @VisibleForTesting
- static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable.
@VisibleForTesting
enum StatsType {
@@ -154,14 +154,6 @@
@VisibleForTesting
public abstract static class Dependencies {
- /**
- * Get polling Interval in milliseconds.
- */
- public int getPerformPollInterval() {
- // TODO: Consider make this configurable.
- return DEFAULT_PERFORM_POLL_INTERVAL_MS;
- }
-
/** Get handler. */
@NonNull public abstract Handler getHandler();
@@ -403,6 +395,7 @@
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();
@@ -745,6 +738,17 @@
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() {
if (!mPollingStarted) return;
@@ -752,7 +756,7 @@
mHandler.removeCallbacks(mScheduledPollingTask);
}
- mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval());
+ mHandler.postDelayed(mScheduledPollingTask, getPollingInterval());
}
// Return forwarding rule map. This is used for testing only.
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 31d9814..64242ae 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -25,11 +25,10 @@
import static android.net.NetworkStats.UID_TETHERING;
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.STATS_PER_IFACE;
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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -104,11 +103,6 @@
private final TestLooper mTestLooper = new TestLooper();
private BpfCoordinator.Dependencies mDeps =
new BpfCoordinator.Dependencies() {
- @Override
- public int getPerformPollInterval() {
- return DEFAULT_PERFORM_POLL_INTERVAL_MS;
- }
-
@NonNull
public Handler getHandler() {
return new Handler(mTestLooper.getLooper());
@@ -183,9 +177,11 @@
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 {
when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
}
@@ -254,7 +250,7 @@
clearInvocations(mNetd);
// Verify the polling update thread stopped.
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
verify(mNetd, never()).tetherOffloadGetStats();
}
@@ -279,20 +275,20 @@
when(mNetd.tetherOffloadGetStats()).thenReturn(
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
mTetherStatsProvider.onSetAlert(100);
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
mTetherStatsProviderCb.assertNoCallback();
// Verify that notifyAlertReached fired when quota is reached.
when(mNetd.tetherOffloadGetStats()).thenReturn(
new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
mTetherStatsProviderCb.expectNotifyAlertReached();
// Verify that set quota with UNLIMITED won't trigger any callback.
mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED);
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
mTetherStatsProviderCb.assertNoCallback();
}
@@ -512,7 +508,7 @@
coordinator.startPolling();
// The tether stats polling task should not be scheduled.
- mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
waitForIdle();
verify(mNetd, never()).tetherOffloadGetStats();
@@ -559,4 +555,53 @@
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();
+ }
}