From 9fc333024f4f74923a61f01831d76878b209257f Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Wed, 25 May 2022 10:26:05 +0800 Subject: [PATCH 1/2] Add unit test for MultipathPolicyTracker This CL adds unit test to test onThresholdReached event would unregister and re-register usage callback. Ignore-AOSP-First: Urgent fix Test: MultipathPolicyTrackerTest#testOnThresholdReached Bug: 233733267 Change-Id: I6e43ae93055009d91b0795a4466f2c02f4403c0a --- .../MultipathPolicyTrackerTest.java | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java index ec515376af..d1bf40ed56 100644 --- a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java +++ b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java @@ -27,6 +27,7 @@ import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_Q import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; +import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -184,7 +185,7 @@ public class MultipathPolicyTrackerTest { (int) setting); } - private void testGetMultipathPreference( + private void prepareGetMultipathPreferenceTest( long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit, long defaultGlobalSetting, long defaultResSetting, boolean roaming) { @@ -286,7 +287,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_SubscriptionQuota() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */, DataUnit.MEGABYTES.toBytes(100) /* policyWarning */, @@ -301,7 +302,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_UserWarningQuota() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, OPPORTUNISTIC_QUOTA_UNKNOWN, // Remaining days are 29 days from Apr. 2nd to May 1st. @@ -320,7 +321,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_SnoozedWarningQuota() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, OPPORTUNISTIC_QUOTA_UNKNOWN, POLICY_SNOOZED /* policyWarning */, @@ -339,7 +340,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_SnoozedBothQuota() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, OPPORTUNISTIC_QUOTA_UNKNOWN, // 29 days from Apr. 2nd to May 1st @@ -356,7 +357,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_SettingChanged() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, OPPORTUNISTIC_QUOTA_UNKNOWN, WARNING_DISABLED, @@ -381,7 +382,7 @@ public class MultipathPolicyTrackerTest { @Test public void testGetMultipathPreference_ResourceChanged() { - testGetMultipathPreference( + prepareGetMultipathPreferenceTest( DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, OPPORTUNISTIC_QUOTA_UNKNOWN, WARNING_DISABLED, @@ -404,4 +405,45 @@ public class MultipathPolicyTrackerTest { verify(mStatsManager, times(1)).registerUsageCallback( any(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any()); } + + @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) + @Test + public void testOnThresholdReached() { + prepareGetMultipathPreferenceTest( + DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, + DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */, + DataUnit.MEGABYTES.toBytes(100) /* policyWarning */, + LIMIT_DISABLED, + DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, + 2_500_000 /* defaultResSetting */, + false /* roaming */); + + final ArgumentCaptor usageCallbackCaptor = + ArgumentCaptor.forClass(NetworkStatsManager.UsageCallback.class); + final ArgumentCaptor networkTemplateCaptor = + ArgumentCaptor.forClass(NetworkTemplate.class); + // Verify the callback is registered with quota - used = 14 - 2 = 12MB. + verify(mStatsManager, times(1)).registerUsageCallback( + networkTemplateCaptor.capture(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), + usageCallbackCaptor.capture()); + + // Capture arguments for later use. + final NetworkStatsManager.UsageCallback usageCallback = usageCallbackCaptor.getValue(); + final NetworkTemplate template = networkTemplateCaptor.getValue(); + assertNotNull(usageCallback); + assertNotNull(template); + + // Decrease quota from 14 to 11, and trigger the event. + // TODO: Mock daily and monthly used bytes instead of changing subscription to simulate + // remaining quota changed. + when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH)) + .thenReturn(DataUnit.MEGABYTES.toBytes(11)); + usageCallback.onThresholdReached(template); + + // Callback must have been re-registered with new remaining quota = 11 - 2 = 9MB. + verify(mStatsManager, times(1)) + .unregisterUsageCallback(eq(usageCallback)); + verify(mStatsManager, times(1)).registerUsageCallback( + eq(template), eq(DataUnit.MEGABYTES.toBytes(9)), any(), eq(usageCallback)); + } } From e971f1528873ec31152f41dbbd2f8cb4e571f656 Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Tue, 31 May 2022 20:12:04 +0800 Subject: [PATCH 2/2] Fix get summary does not include the latest bucket In internalGetSummaryForNetwork, the history object will not include the latest bucket because of the logic in NetworkStatsHistory#recordHistory would only include buckets that atomically occur in the inclusive time range. This makes querySummaryForDevice callers who are also sensitive to the latest bucket fails. This CL revert the behavior back to S, in T this is temporarily changed after aosp/1937268. Also, for test purpose, this CL change System.currentTimeMillis to use mClock when querying. Ignore-AOSP-First: Urgent fix Test: NetworkStatsServiceTest#testGetLatestSummary Bug: 233733267 Change-Id: I55e2a5d953622b68da36a9b8d3e57c88d57543b0 --- .../server/net/NetworkStatsService.java | 4 +- .../server/net/NetworkStatsServiceTest.java | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java index dc09bc2b67..cf1a53ff20 100644 --- a/service-t/src/com/android/server/net/NetworkStatsService.java +++ b/service-t/src/com/android/server/net/NetworkStatsService.java @@ -1468,9 +1468,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL, - accessLevel, callingUid, start, end); + accessLevel, callingUid, Long.MIN_VALUE, Long.MAX_VALUE); - final long now = System.currentTimeMillis(); + final long now = mClock.millis(); final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); final NetworkStats stats = new NetworkStats(end - start, 1); diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java index d37ae23558..9293e5c7fe 100644 --- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -148,7 +148,10 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.time.Clock; +import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; @@ -1112,6 +1115,40 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0); } + @Test + public void testGetLatestSummary() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Increase arbitrary time which does not align to the bucket edge, create some traffic. + incrementCurrentTime(1751000L); + NetworkStats.Entry entry = new NetworkStats.Entry( + TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 50L, 5L, 51L, 1L, 3L); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1).insertEntry(entry)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // Verify the mocked stats is returned by querying with the range of the latest bucket. + final ZonedDateTime end = + ZonedDateTime.ofInstant(mClock.instant(), ZoneId.systemDefault()); + final ZonedDateTime start = end.truncatedTo(ChronoUnit.HOURS); + NetworkStats stats = mSession.getSummaryForNetwork(buildTemplateWifi(TEST_WIFI_NETWORK_KEY), + start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli()); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 50L, 5L, 51L, 1L, 3L); + + // For getHistoryIntervalForNetwork, only includes buckets that atomically occur in + // the inclusive time range, instead of including the latest bucket. This behavior is + // already documented publicly, refer to {@link NetworkStatsManager#queryDetails}. + } + @Test public void testUidStatsForTransport() throws Exception { // pretend that network comes online