[AF06] Fill file attributes metrics
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.net.NetworkStatsRecorderTest
Bug: 313841499
Change-Id: I12759e14c29917ad61eedad4cfee21f153a1a6bd
diff --git a/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java b/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java
index 2cf49ba..4f5d6be 100644
--- a/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java
+++ b/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java
@@ -29,15 +29,19 @@
import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ConnectivityStatsLog;
+import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* Helper class to log NetworkStats related metrics.
@@ -95,10 +99,46 @@
}
/**
+ * Get file count and total byte count for the given directory and prefix.
+ *
+ * @return File count and total byte count as a pair, or 0s if met errors.
+ */
+ private static Pair<Integer, Integer> getStatsFilesAttributes(
+ @Nullable File statsDir, @NonNull String prefix) {
+ if (statsDir == null) return new Pair<>(0, 0);
+
+ // Only counts the matching files.
+ // The files are named in the following format:
+ // <prefix>.<startTimestamp>-[<endTimestamp>]
+ // e.g. uid_tag.12345-
+ // See FileRotator#FileInfo for more detail.
+ final Pattern pattern = Pattern.compile("^" + prefix + "\\.[0-9]+-[0-9]*$");
+
+ // Ensure that base path exists.
+ statsDir.mkdirs();
+
+ int totalFiles = 0;
+ int totalBytes = 0;
+ for (String name : emptyIfNull(statsDir.list())) {
+ if (!pattern.matcher(name).matches()) continue;
+
+ totalFiles++;
+ // Cast to int is safe since stats persistent files are several MBs in total.
+ totalBytes += (int) (new File(statsDir, name).length());
+
+ }
+ return new Pair<>(totalFiles, totalBytes);
+ }
+
+ private static String [] emptyIfNull(@Nullable String [] array) {
+ return (array == null) ? new String[0] : array;
+ }
+
+ /**
* Log statistics from the NetworkStatsRecorder file reading process into statsd.
*/
public void logRecorderFileReading(@NonNull String prefix, int readLatencyMillis,
- @NonNull NetworkStatsCollection collection) {
+ @Nullable File statsDir, @NonNull NetworkStatsCollection collection) {
final Set<Integer> uids = new HashSet<>();
final Map<NetworkStatsCollection.Key, NetworkStatsHistory> entries =
collection.getEntries();
@@ -112,12 +152,12 @@
totalHistorySize += history.size();
}
- // TODO: Fill file characteristics.
+ final Pair<Integer, Integer> fileAttributes = getStatsFilesAttributes(statsDir, prefix);
mDeps.writeRecorderFileReadingStats(prefixToRecorderType(prefix),
mReadIndex++,
readLatencyMillis,
- 0 /* fileCount */,
- 0 /* totalFileSize */,
+ fileAttributes.first /* fileCount */,
+ fileAttributes.second /* 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 456a212..0fe13f0 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -22,6 +22,7 @@
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
@@ -45,6 +46,7 @@
import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -93,6 +95,8 @@
private WeakReference<NetworkStatsCollection> mComplete;
private final NetworkStatsMetricsLogger mMetricsLogger = new NetworkStatsMetricsLogger();
+ @Nullable
+ private final File mStatsDir;
/**
* Non-persisted recorder, with only one bucket. Used by {@link NetworkStatsObservers}.
@@ -114,6 +118,7 @@
mSinceBoot = new NetworkStatsCollection(mBucketDuration);
mPendingRewriter = null;
+ mStatsDir = null;
}
/**
@@ -121,7 +126,7 @@
*/
public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags,
- boolean wipeOnError, boolean useFastDataInput) {
+ boolean wipeOnError, boolean useFastDataInput, @Nullable File statsDir) {
mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
@@ -136,6 +141,7 @@
mSinceBoot = new NetworkStatsCollection(bucketDuration);
mPendingRewriter = new CombiningRewriter(mPending);
+ mStatsDir = statsDir;
}
public void setPersistThreshold(long thresholdBytes) {
@@ -192,7 +198,8 @@
// 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);
+ mMetricsLogger.logRecorderFileReading(mCookie, (int) (readEnd - readStart),
+ mStatsDir, res);
}
}
return res;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index ad83e7f..ce7fd7b 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -969,7 +969,7 @@
return new NetworkStatsRecorder(new FileRotator(
baseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags,
- wipeOnError, false /* useFastDataInput */);
+ wipeOnError, false /* useFastDataInput */, baseDir);
}
@GuardedBy("mStatsLock")
diff --git a/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt b/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt
index c3ea9f4..57602f1 100644
--- a/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt
+++ b/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt
@@ -156,7 +156,8 @@
UID_COLLECTION_BUCKET_DURATION_MS,
false /* includeTags */,
false /* wipeOnError */,
- useFastDataInput /* useFastDataInput */
+ useFastDataInput /* useFastDataInput */,
+ it
)
recorder.orLoadCompleteLocked
}
diff --git a/tests/unit/java/android/net/NetworkStatsRecorderTest.java b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
index 9ec9a79..afed227 100644
--- a/tests/unit/java/android/net/NetworkStatsRecorderTest.java
+++ b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
@@ -20,10 +20,12 @@
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_UID_TAG;
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_UIDTAG;
import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
@@ -36,6 +38,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.NonNull;
import android.net.NetworkIdentity;
import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
@@ -49,12 +52,17 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import libcore.testing.io.TestIoUtils;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
@RunWith(DevSdkIgnoreRunner.class)
@@ -78,7 +86,7 @@
private NetworkStatsRecorder buildRecorder(FileRotator rotator, boolean wipeOnError) {
return new NetworkStatsRecorder(rotator, mObserver, mDropBox, TEST_PREFIX,
HOUR_IN_MILLIS, false /* includeTags */, wipeOnError,
- false /* useFastDataInput */);
+ false /* useFastDataInput */, null /* baseDir */);
}
@Test
@@ -106,7 +114,7 @@
final NetworkStatsMetricsLogger.Dependencies deps =
mock(NetworkStatsMetricsLogger.Dependencies.class);
final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps);
- logger.logRecorderFileReading(PREFIX_XT, 888, collection);
+ logger.logRecorderFileReading(PREFIX_XT, 888, null /* statsDir */, collection);
verify(deps).writeRecorderFileReadingStats(
NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT,
1 /* readIndex */,
@@ -119,7 +127,7 @@
);
// Write second time, verify the index increases.
- logger.logRecorderFileReading(PREFIX_XT, 567, collection);
+ logger.logRecorderFileReading(PREFIX_XT, 567, null /* statsDir */, collection);
verify(deps).writeRecorderFileReadingStats(
NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT,
2 /* readIndex */,
@@ -148,7 +156,7 @@
final NetworkStatsMetricsLogger.Dependencies deps =
mock(NetworkStatsMetricsLogger.Dependencies.class);
final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps);
- logger.logRecorderFileReading(PREFIX_UID, 123, collection);
+ logger.logRecorderFileReading(PREFIX_UID, 123, null /* statsDir */, collection);
verify(deps).writeRecorderFileReadingStats(
NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID,
1 /* readIndex */,
@@ -160,4 +168,41 @@
5 /* totalHistorySize */
);
}
+
+ @Test
+ public void testFileReadingMetrics_fileAttributes() throws IOException {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30);
+
+ // Create files for testing. Only the first and the third files should be counted,
+ // with total 26 (each char takes 2 bytes) bytes in the content.
+ final File statsDir = TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
+ write(statsDir, "uid_tag.1024-2048", "wanted");
+ write(statsDir, "uid_tag.1024-2048.backup", "");
+ write(statsDir, "uid_tag.2048-", "wanted2");
+ write(statsDir, "uid.2048-4096", "unwanted");
+ write(statsDir, "uid.2048-4096.backup", "unwanted2");
+
+ final NetworkStatsMetricsLogger.Dependencies deps =
+ mock(NetworkStatsMetricsLogger.Dependencies.class);
+ final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps);
+ logger.logRecorderFileReading(PREFIX_UID_TAG, 678, statsDir, collection);
+ verify(deps).writeRecorderFileReadingStats(
+ NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG,
+ 1 /* readIndex */,
+ 678 /* readLatencyMillis */,
+ 2 /* fileCount */,
+ 26 /* totalFileSize */,
+ 0 /* keys */,
+ 0 /* uids */,
+ 0 /* totalHistorySize */
+ );
+ }
+
+ private void write(@NonNull File baseDir, @NonNull String name,
+ @NonNull String value) throws IOException {
+ final DataOutputStream out = new DataOutputStream(
+ new FileOutputStream(new File(baseDir, name)));
+ out.writeChars(value);
+ out.close();
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 19216a7..9e6beb8 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -2231,7 +2231,7 @@
return new NetworkStatsRecorder(new FileRotator(
directory, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
observer, dropBox, prefix, config.bucketDuration, includeTags, wipeOnError,
- false /* useFastDataInput */);
+ false /* useFastDataInput */, directory);
}
private NetworkStatsCollection getLegacyCollection(String prefix, boolean includeTags) {