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);
+    }
 }