[AF05] Fill NetworkStatsRecorderFileReading metrics
By verifying with benchmark tests, this does not have significant
change on file reading performance.
Test: atest ConnectivityBenchmarkTests
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.net.NetworkStatsRecorderTest
Bug: 313841499
Change-Id: I161a101dcec82fdaa70128541f9e153bac484ecc
diff --git a/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java b/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java
new file mode 100644
index 0000000..2cf49ba
--- /dev/null
+++ b/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.metrics;
+
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
+
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_DISABLED;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__OPERATION_TYPE__ROT_READ;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UNKNOWN;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT;
+
+import android.annotation.NonNull;
+import android.net.NetworkStatsCollection;
+import android.net.NetworkStatsHistory;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ConnectivityStatsLog;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class to log NetworkStats related metrics.
+ *
+ * This class does not provide thread-safe.
+ */
+public class NetworkStatsMetricsLogger {
+ final Dependencies mDeps;
+ int mReadIndex = 1;
+
+ /** Dependency class */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Writes a NETWORK_STATS_RECORDER_FILE_OPERATION_REPORTED event to ConnectivityStatsLog.
+ */
+ public void writeRecorderFileReadingStats(int recorderType, int readIndex,
+ int readLatencyMillis,
+ int fileCount, int totalFileSize,
+ int keys, int uids, int totalHistorySize) {
+ ConnectivityStatsLog.write(NETWORK_STATS_RECORDER_FILE_OPERATED,
+ NETWORK_STATS_RECORDER_FILE_OPERATED__OPERATION_TYPE__ROT_READ,
+ recorderType,
+ readIndex,
+ readLatencyMillis,
+ fileCount,
+ totalFileSize,
+ keys,
+ uids,
+ totalHistorySize,
+ NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_DISABLED);
+ }
+ }
+
+ public NetworkStatsMetricsLogger() {
+ mDeps = new Dependencies();
+ }
+
+ @VisibleForTesting
+ public NetworkStatsMetricsLogger(Dependencies deps) {
+ mDeps = deps;
+ }
+
+ private static int prefixToRecorderType(@NonNull String prefix) {
+ switch (prefix) {
+ case PREFIX_XT:
+ return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT;
+ case PREFIX_UID:
+ return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID;
+ case PREFIX_UID_TAG:
+ return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG;
+ default:
+ return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UNKNOWN;
+ }
+ }
+
+ /**
+ * Log statistics from the NetworkStatsRecorder file reading process into statsd.
+ */
+ public void logRecorderFileReading(@NonNull String prefix, int readLatencyMillis,
+ @NonNull NetworkStatsCollection collection) {
+ final Set<Integer> uids = new HashSet<>();
+ final Map<NetworkStatsCollection.Key, NetworkStatsHistory> entries =
+ collection.getEntries();
+
+ for (final NetworkStatsCollection.Key key : entries.keySet()) {
+ uids.add(key.uid);
+ }
+
+ int totalHistorySize = 0;
+ for (final NetworkStatsHistory history : entries.values()) {
+ totalHistorySize += history.size();
+ }
+
+ // TODO: Fill file characteristics.
+ mDeps.writeRecorderFileReadingStats(prefixToRecorderType(prefix),
+ mReadIndex++,
+ readLatencyMillis,
+ 0 /* fileCount */,
+ 0 /* totalFileSize */,
+ entries.size(),
+ uids.size(),
+ totalHistorySize);
+ }
+}
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index 5f9b65c..456a212 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -32,12 +32,14 @@
import android.net.TrafficStats;
import android.os.Binder;
import android.os.DropBoxManager;
+import android.os.SystemClock;
import android.service.NetworkStatsRecorderProto;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FileRotator;
+import com.android.metrics.NetworkStatsMetricsLogger;
import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
@@ -90,6 +92,7 @@
private final CombiningRewriter mPendingRewriter;
private WeakReference<NetworkStatsCollection> mComplete;
+ private final NetworkStatsMetricsLogger mMetricsLogger = new NetworkStatsMetricsLogger();
/**
* Non-persisted recorder, with only one bucket. Used by {@link NetworkStatsObservers}.
@@ -182,8 +185,15 @@
Objects.requireNonNull(mRotator, "missing FileRotator");
NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
if (res == null) {
+ final long readStart = SystemClock.elapsedRealtime();
res = loadLocked(Long.MIN_VALUE, Long.MAX_VALUE);
mComplete = new WeakReference<NetworkStatsCollection>(res);
+ final long readEnd = SystemClock.elapsedRealtime();
+ // For legacy recorders which are used for data integrity check, which
+ // have wipeOnError flag unset, skip reporting metrics.
+ if (mWipeOnError) {
+ mMetricsLogger.logRecorderFileReading(mCookie, (int) (readEnd - readStart), res);
+ }
}
return res;
}
diff --git a/tests/unit/java/android/net/NetworkStatsRecorderTest.java b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
index e8f853e..9ec9a79 100644
--- a/tests/unit/java/android/net/NetworkStatsRecorderTest.java
+++ b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
@@ -16,8 +16,15 @@
package com.android.server.net;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID;
+import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.mockito.Mockito.any;
@@ -29,12 +36,16 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
+import android.net.NetworkStatsCollection;
import android.os.DropBoxManager;
import androidx.test.filters.SmallTest;
import com.android.internal.util.FileRotator;
+import com.android.metrics.NetworkStatsMetricsLogger;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -53,6 +64,8 @@
private static final String TAG = NetworkStatsRecorderTest.class.getSimpleName();
private static final String TEST_PREFIX = "test";
+ private static final int TEST_UID1 = 1234;
+ private static final int TEST_UID2 = 1235;
@Mock private DropBoxManager mDropBox;
@Mock private NetworkStats.NonMonotonicObserver mObserver;
@@ -86,4 +99,65 @@
// Verify that the rotator won't delete files.
verify(rotator, never()).deleteAll();
}
+
+ @Test
+ public void testFileReadingMetrics_empty() {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30);
+ final NetworkStatsMetricsLogger.Dependencies deps =
+ mock(NetworkStatsMetricsLogger.Dependencies.class);
+ final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps);
+ logger.logRecorderFileReading(PREFIX_XT, 888, collection);
+ verify(deps).writeRecorderFileReadingStats(
+ NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT,
+ 1 /* readIndex */,
+ 888 /* readLatencyMillis */,
+ 0 /* fileCount */,
+ 0 /* totalFileSize */,
+ 0 /* keys */,
+ 0 /* uids */,
+ 0 /* totalHistorySize */
+ );
+
+ // Write second time, verify the index increases.
+ logger.logRecorderFileReading(PREFIX_XT, 567, collection);
+ verify(deps).writeRecorderFileReadingStats(
+ NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT,
+ 2 /* readIndex */,
+ 567 /* readLatencyMillis */,
+ 0 /* fileCount */,
+ 0 /* totalFileSize */,
+ 0 /* keys */,
+ 0 /* uids */,
+ 0 /* totalHistorySize */
+ );
+ }
+
+ @Test
+ public void testFileReadingMetrics() {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ final NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity.Builder().build());
+ // Empty entries will be skipped, put some ints to make sure they can be recorded.
+ entry.rxBytes = 1;
+
+ collection.recordData(identSet, TEST_UID1, SET_DEFAULT, TAG_NONE, 0, 60, entry);
+ collection.recordData(identSet, TEST_UID2, SET_DEFAULT, TAG_NONE, 0, 60, entry);
+ collection.recordData(identSet, TEST_UID2, SET_FOREGROUND, TAG_NONE, 30, 60, entry);
+
+ final NetworkStatsMetricsLogger.Dependencies deps =
+ mock(NetworkStatsMetricsLogger.Dependencies.class);
+ final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps);
+ logger.logRecorderFileReading(PREFIX_UID, 123, collection);
+ verify(deps).writeRecorderFileReadingStats(
+ NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID,
+ 1 /* readIndex */,
+ 123 /* readLatencyMillis */,
+ 0 /* fileCount */,
+ 0 /* totalFileSize */,
+ 3 /* keys */,
+ 2 /* uids */,
+ 5 /* totalHistorySize */
+ );
+ }
}