From 207b125a20c54ee330e2774315094fa1c0caf57f 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. Test: MultipathPolicyTrackerTest#testOnThresholdReached Bug: 233733267 Change-Id: I6e43ae93055009d91b0795a4466f2c02f4403c0a Merged-In: I6e43ae93055009d91b0795a4466f2c02f4403c0a (pure cherry-picked from ag/18579049) --- .../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 04b2cb2f2d506a6bf60d1c384a648fb59f59ff49 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. Test: NetworkStatsServiceTest#testGetLatestSummary Bug: 233733267 Change-Id: I55e2a5d953622b68da36a9b8d3e57c88d57543b0 Merged-In: I55e2a5d953622b68da36a9b8d3e57c88d57543b0 (pure cherry-picked from ag/18670248) --- .../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 63e6501c41..36993ba359 100644 --- a/service-t/src/com/android/server/net/NetworkStatsService.java +++ b/service-t/src/com/android/server/net/NetworkStatsService.java @@ -1455,9 +1455,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 f1820b3acb..32924b83cd 100644 --- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -144,7 +144,10 @@ import java.io.File; 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; @@ -1104,6 +1107,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