Loading packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +15 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static android.net.NetworkStats.UID_ALL; 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; Loading Loading @@ -69,8 +71,6 @@ import java.util.Objects; 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 { Loading Loading @@ -154,14 +154,6 @@ public class BpfCoordinator { @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(); Loading Loading @@ -403,6 +395,7 @@ public class BpfCoordinator { 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(); Loading Loading @@ -745,6 +738,17 @@ public class BpfCoordinator { 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; Loading @@ -752,7 +756,7 @@ public class BpfCoordinator { mHandler.removeCallbacks(mScheduledPollingTask); } mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); } // Return forwarding rule map. This is used for testing only. Loading packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +58 −13 Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ import static android.net.NetworkStats.UID_ALL; 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; Loading Loading @@ -104,11 +103,6 @@ public class BpfCoordinatorTest { 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()); Loading Loading @@ -183,9 +177,11 @@ public class BpfCoordinatorTest { 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(); } Loading Loading @@ -254,7 +250,7 @@ public class BpfCoordinatorTest { 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(); } Loading @@ -279,20 +275,20 @@ public class BpfCoordinatorTest { 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(); } Loading Loading @@ -512,7 +508,7 @@ public class BpfCoordinatorTest { 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(); Loading Loading @@ -559,4 +555,53 @@ public class BpfCoordinatorTest { 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(); } } Loading
packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +15 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static android.net.NetworkStats.UID_ALL; 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; Loading Loading @@ -69,8 +71,6 @@ import java.util.Objects; 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 { Loading Loading @@ -154,14 +154,6 @@ public class BpfCoordinator { @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(); Loading Loading @@ -403,6 +395,7 @@ public class BpfCoordinator { 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(); Loading Loading @@ -745,6 +738,17 @@ public class BpfCoordinator { 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; Loading @@ -752,7 +756,7 @@ public class BpfCoordinator { mHandler.removeCallbacks(mScheduledPollingTask); } mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval()); mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); } // Return forwarding rule map. This is used for testing only. Loading
packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +58 −13 Original line number Diff line number Diff line Loading @@ -25,11 +25,10 @@ import static android.net.NetworkStats.UID_ALL; 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; Loading Loading @@ -104,11 +103,6 @@ public class BpfCoordinatorTest { 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()); Loading Loading @@ -183,9 +177,11 @@ public class BpfCoordinatorTest { 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(); } Loading Loading @@ -254,7 +250,7 @@ public class BpfCoordinatorTest { 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(); } Loading @@ -279,20 +275,20 @@ public class BpfCoordinatorTest { 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(); } Loading Loading @@ -512,7 +508,7 @@ public class BpfCoordinatorTest { 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(); Loading Loading @@ -559,4 +555,53 @@ public class BpfCoordinatorTest { 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(); } }