diff --git a/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java b/service-t/src/com/android/metrics/NetworkStatsMetricsLogger.java index 2cf49ba5b0b9e20ecc2b2fc17446fd6f0a641214..4f5d6be4195196dfd750300b5cd3eebc1414a660 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_FIL 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. @@ -94,11 +98,47 @@ public class NetworkStatsMetricsLogger { } } + /** + * 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 @@ public class NetworkStatsMetricsLogger { 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 456a212cdbade5028c3c6a00943c8c0d083e1ed8..0fe13f098a8866f118e0ae3a39d77ea5545e227e 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.net.TrafficStats.MB_IN_BYTES; 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 com.android.net.module.util.NetworkStatsUtils; 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 @@ public class NetworkStatsRecorder { 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 @@ public class NetworkStatsRecorder { mSinceBoot = new NetworkStatsCollection(mBucketDuration); mPendingRewriter = null; + mStatsDir = null; } /** @@ -121,7 +126,7 @@ public class NetworkStatsRecorder { */ 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 @@ public class NetworkStatsRecorder { mSinceBoot = new NetworkStatsCollection(bucketDuration); mPendingRewriter = new CombiningRewriter(mPending); + mStatsDir = statsDir; } public void setPersistThreshold(long thresholdBytes) { @@ -192,7 +198,8 @@ public class NetworkStatsRecorder { // 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 ad83e7f6c8cabc7bc395150884440b5b85dc2520..ce7fd7b4b6e0ce5b5119a11a869fe2a735884264 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 c3ea9f40a9837c9879d3edb494efc61a655f50cc..57602f1017d9a30c88b71054c4a5216984fe79a5 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 @@ class NetworkStatsTest { 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 9ec9a7919ef20af5c43e6ea22faa6a62bf225b8f..afed227eedbe81f0b5e3eb61eeaebe203619c4c6 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_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_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.reset; 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.metrics.NetworkStatsMetricsLogger; 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 @@ public final class NetworkStatsRecorderTest { 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 @@ public final class NetworkStatsRecorderTest { 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 @@ public final class NetworkStatsRecorderTest { ); // 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 @@ public final class NetworkStatsRecorderTest { 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 @@ public final class NetworkStatsRecorderTest { 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 19216a73cba35eef73d3d69420a4a4e847bf7408..9e6beb87a8ebf37bcc47e1e76d56d9d39275a43c 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 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { 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) {