Calculate and populate TX/RX bytes for each upstream in TetheringMetrics
Bug: 282861165
Test: statsd_testdrive 303
Test: atest TetheringMetricsTest
Change-Id: I9a358dfc22640f309ca52378ad826cd7dbcee9b3
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index 9614785..3673e51 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -104,7 +104,7 @@
private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
- private final ArrayMap<UpstreamType, DataUsage> mUpstreamDataUsage = new ArrayMap<>();
+ private final ArrayMap<UpstreamType, DataUsage> mUpstreamUsageBaseline = new ArrayMap<>();
private final Context mContext;
private final Dependencies mDependencies;
private final NetworkStatsManager mNetworkStatsManager;
@@ -173,6 +173,13 @@
this.rxBytes = rxBytes;
}
+ /*** Calculate the data usage delta from give new and old usage */
+ public static DataUsage subtract(DataUsage newUsage, DataUsage oldUsage) {
+ return new DataUsage(
+ newUsage.txBytes - oldUsage.txBytes,
+ newUsage.rxBytes - oldUsage.rxBytes);
+ }
+
@Override
public int hashCode() {
return (int) (txBytes & 0xFFFFFFFF)
@@ -244,11 +251,24 @@
statsBuilder.setErrorCode(errorCodeToEnum(errCode));
}
- private DataUsage calculateDataUsage(@Nullable UpstreamType upstream) {
+ /**
+ * Calculates the data usage difference between the current and previous usage for the
+ * specified upstream type.
+ *
+ * @return A DataUsage object containing the calculated difference in transmitted (tx) and
+ * received (rx) bytes.
+ */
+ private DataUsage calculateDataUsageDelta(@Nullable UpstreamType upstream) {
if (upstream != null && mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)
&& isUsageSupportedForUpstreamType(upstream)) {
- // TODO: Implement data usage calculation for the upstream type.
- return EMPTY;
+ final DataUsage oldUsage = mUpstreamUsageBaseline.getOrDefault(upstream, EMPTY);
+ if (oldUsage.equals(EMPTY)) {
+ Log.d(TAG, "No usage baseline for the upstream=" + upstream);
+ return EMPTY;
+ }
+ // TODO(b/352537247): Fix data usage which might be incorrect if the device uses
+ // tethering with the same upstream for over 15 days.
+ return DataUsage.subtract(getCurrentDataUsageForUpstreamType(upstream), oldUsage);
}
return EMPTY;
}
@@ -264,7 +284,7 @@
final long newTime = mDependencies.timeNow();
if (mCurrentUpstream != null) {
- final DataUsage dataUsage = calculateDataUsage(upstream);
+ final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
mCurrentUpstream, dataUsage));
}
@@ -322,7 +342,7 @@
final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
final long stopTime = mDependencies.timeNow();
// Handle the last upstream event.
- final DataUsage dataUsage = calculateDataUsage(mCurrentUpstream);
+ final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream,
dataUsage.txBytes, dataUsage.rxBytes);
statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
@@ -387,20 +407,20 @@
*/
public void initUpstreamUsageBaseline() {
if (!(mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)
- && mUpstreamDataUsage.isEmpty())) {
+ && mUpstreamUsageBaseline.isEmpty())) {
return;
}
for (UpstreamType type : UpstreamType.values()) {
if (!isUsageSupportedForUpstreamType(type)) continue;
- mUpstreamDataUsage.put(type, getCurrentDataUsageForUpstreamType(type));
+ mUpstreamUsageBaseline.put(type, getCurrentDataUsageForUpstreamType(type));
}
}
@VisibleForTesting
@NonNull
DataUsage getDataUsageFromUpstreamType(@NonNull UpstreamType type) {
- return mUpstreamDataUsage.getOrDefault(type, EMPTY);
+ return mUpstreamUsageBaseline.getOrDefault(type, EMPTY);
}
@@ -431,7 +451,7 @@
mUpstreamEventList.clear();
mCurrentUpstream = null;
mCurrentUpStreamStartTime = 0L;
- mUpstreamDataUsage.clear();
+ mUpstreamUsageBaseline.clear();
}
private DownstreamType downstreamTypeToEnum(final int ifaceType) {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index 59373a4..e875c4c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -120,6 +120,7 @@
private TetheringMetrics mTetheringMetrics;
private final NetworkTetheringReported.Builder mStatsBuilder =
NetworkTetheringReported.newBuilder();
+ private final ArrayMap<UpstreamType, DataUsage> mMockUpstreamUsageBaseline = new ArrayMap<>();
private long mElapsedRealtime;
@@ -147,6 +148,18 @@
MockitoAnnotations.initMocks(this);
doReturn(TEST_START_TIME).when(mDeps).timeNow();
doReturn(mNetworkStatsManager).when(mContext).getSystemService(NetworkStatsManager.class);
+ // Set up the usage for upstream types.
+ mMockUpstreamUsageBaseline.put(UT_CELLULAR, new DataUsage(100L, 200L));
+ mMockUpstreamUsageBaseline.put(UT_WIFI, new DataUsage(400L, 800L));
+ mMockUpstreamUsageBaseline.put(UT_BLUETOOTH, new DataUsage(50L, 80L));
+ mMockUpstreamUsageBaseline.put(UT_ETHERNET, new DataUsage(0L, 0L));
+ doAnswer(inv -> {
+ final NetworkTemplate template = (NetworkTemplate) inv.getArguments()[0];
+ final DataUsage dataUsage = mMockUpstreamUsageBaseline.getOrDefault(
+ matchRuleToUpstreamType(template.getMatchRule()), new DataUsage(0L, 0L));
+ return makeNetworkStatsWithTxRxBytes(dataUsage);
+ }).when(mNetworkStatsManager).queryDetailsForUidTagState(any(), eq(Long.MIN_VALUE),
+ eq(Long.MAX_VALUE), eq(UID_TETHERING), eq(TAG_NONE), eq(STATE_ALL));
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mElapsedRealtime = 0L;
}
@@ -493,33 +506,24 @@
}
}
+ private void initializeUpstreamUsageBaseline() {
+ doReturn(true).when(mDeps).isUpstreamDataUsageMetricsEnabled(any());
+ mTetheringMetrics.initUpstreamUsageBaseline();
+ }
+
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testInitUpstreamUsageBaselineAndCleanup() {
// Verify the usage is empty for all upstream types before initialization.
verifyEmptyUsageForAllUpstreamTypes();
- // Set up the usage for upstream types.
- final ArrayMap<UpstreamType, DataUsage> upstreamDataUsage = new ArrayMap<>();
- upstreamDataUsage.put(UT_CELLULAR, new DataUsage(100L, 200L));
- upstreamDataUsage.put(UT_WIFI, new DataUsage(400L, 800L));
- upstreamDataUsage.put(UT_BLUETOOTH, new DataUsage(50L, 80L));
- upstreamDataUsage.put(UT_ETHERNET, new DataUsage(0L, 0L));
- doAnswer(inv -> {
- final NetworkTemplate template = (NetworkTemplate) inv.getArguments()[0];
- final DataUsage dataUsage = upstreamDataUsage.getOrDefault(
- matchRuleToUpstreamType(template.getMatchRule()), new DataUsage(0L, 0L));
- return makeNetworkStatsWithTxRxBytes(dataUsage);
- }).when(mNetworkStatsManager).queryDetailsForUidTagState(any(), eq(Long.MIN_VALUE),
- eq(Long.MAX_VALUE), eq(UID_TETHERING), eq(TAG_NONE), eq(STATE_ALL));
-
// Verify the usage has been initialized
- doReturn(true).when(mDeps).isUpstreamDataUsageMetricsEnabled(any());
- mTetheringMetrics.initUpstreamUsageBaseline();
+ initializeUpstreamUsageBaseline();
+
for (UpstreamType type : UpstreamType.values()) {
final DataUsage dataUsage = mTetheringMetrics.getDataUsageFromUpstreamType(type);
if (TetheringMetrics.isUsageSupportedForUpstreamType(type)) {
- assertEquals(upstreamDataUsage.get(type), dataUsage);
+ assertEquals(mMockUpstreamUsageBaseline.get(type), dataUsage);
} else {
assertEquals(EMPTY, dataUsage);
}
@@ -529,4 +533,46 @@
mTetheringMetrics.cleanup();
verifyEmptyUsageForAllUpstreamTypes();
}
+
+ private void updateUpstreamDataUsage(UpstreamType type, long usageDiff) {
+ final DataUsage oldWifiUsage = mMockUpstreamUsageBaseline.get(type);
+ final DataUsage newWifiUsage = new DataUsage(
+ oldWifiUsage.txBytes + usageDiff,
+ oldWifiUsage.rxBytes + usageDiff);
+ mMockUpstreamUsageBaseline.put(type, newWifiUsage);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDataUsageCalculation() throws Exception {
+ initializeUpstreamUsageBaseline();
+ mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+ final long wifiTetheringStartTime = currentTimeMillis();
+ incrementCurrentTime(1 * SECOND_IN_MILLIS);
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+ final long wifiDuration = 5 * SECOND_IN_MILLIS;
+ final long wifiUsageDiff = 100L;
+ incrementCurrentTime(wifiDuration);
+ updateUpstreamDataUsage(UT_WIFI, wifiUsageDiff);
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_BLUETOOTH));
+ final long bluetoothDuration = 15 * SECOND_IN_MILLIS;
+ final long btUsageDiff = 50L;
+ incrementCurrentTime(bluetoothDuration);
+ updateUpstreamDataUsage(UT_BLUETOOTH, btUsageDiff);
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_CELLULAR));
+ final long cellDuration = 20 * SECOND_IN_MILLIS;
+ final long cellUsageDiff = 500L;
+ incrementCurrentTime(cellDuration);
+ updateUpstreamDataUsage(UT_CELLULAR, cellUsageDiff);
+ updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+
+ UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+ addUpstreamEvent(upstreamEvents, UT_WIFI, wifiDuration, wifiUsageDiff, wifiUsageDiff);
+ addUpstreamEvent(upstreamEvents, UT_BLUETOOTH, bluetoothDuration, btUsageDiff, btUsageDiff);
+ addUpstreamEvent(upstreamEvents, UT_CELLULAR, cellDuration, cellUsageDiff, cellUsageDiff);
+
+ verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
+ UserType.USER_SETTINGS, upstreamEvents,
+ currentTimeMillis() - wifiTetheringStartTime);
+ }
}