[AF02.1] Support read by FastDataInput in the NetworkStatsRecorder
This increased reading performance by 35% while running
benchmark tests. But the feature is not enabled in the production
code until the service turns it on.
Test: atest ConnectivityBenchmarkTests
Test: atest ConnectivityCoverageTests:android.net.connectivity.android.net.NetworkStatsCollectionTest
Bug: 313841499
Change-Id: Ic827e6c4848cc31d46d62aa93993b483114a1f12
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index deda74e..0e1921a 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -57,6 +57,10 @@
"app-compat-annotations",
"androidx.annotation_annotation",
],
+ static_libs: [
+ // Cannot go to framework-connectivity because mid_sdk checks require 31.
+ "modules-utils-binary-xml",
+ ],
impl_only_libs: [
// The build system will use framework-bluetooth module_current stubs, because
// of sdk_version: "module_current" above.
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index b6f6dbb..cb8328b 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -60,6 +60,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
+import com.android.modules.utils.FastDataInput;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
@@ -116,15 +117,28 @@
private long mEndMillis;
private long mTotalBytes;
private boolean mDirty;
+ private final boolean mUseFastDataInput;
/**
* Construct a {@link NetworkStatsCollection} object.
*
- * @param bucketDuration duration of the buckets in this object, in milliseconds.
+ * @param bucketDurationMillis duration of the buckets in this object, in milliseconds.
* @hide
*/
public NetworkStatsCollection(long bucketDurationMillis) {
+ this(bucketDurationMillis, false /* useFastDataInput */);
+ }
+
+ /**
+ * Construct a {@link NetworkStatsCollection} object.
+ *
+ * @param bucketDurationMillis duration of the buckets in this object, in milliseconds.
+ * @param useFastDataInput true if using {@link FastDataInput} is preferred. Otherwise, false.
+ * @hide
+ */
+ public NetworkStatsCollection(long bucketDurationMillis, boolean useFastDataInput) {
mBucketDurationMillis = bucketDurationMillis;
+ mUseFastDataInput = useFastDataInput;
reset();
}
@@ -483,7 +497,11 @@
/** @hide */
@Override
public void read(InputStream in) throws IOException {
- read((DataInput) new DataInputStream(in));
+ if (mUseFastDataInput) {
+ read(FastDataInput.obtain(in));
+ } else {
+ read((DataInput) new DataInputStream(in));
+ }
}
private void read(DataInput in) throws IOException {
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index 3da1585..5f9b65c 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -79,6 +79,7 @@
private final long mBucketDuration;
private final boolean mOnlyTags;
private final boolean mWipeOnError;
+ private final boolean mUseFastDataInput;
private long mPersistThresholdBytes = 2 * MB_IN_BYTES;
private NetworkStats mLastSnapshot;
@@ -104,6 +105,7 @@
mBucketDuration = YEAR_IN_MILLIS;
mOnlyTags = false;
mWipeOnError = true;
+ mUseFastDataInput = false;
mPending = null;
mSinceBoot = new NetworkStatsCollection(mBucketDuration);
@@ -116,7 +118,7 @@
*/
public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags,
- boolean wipeOnError) {
+ boolean wipeOnError, boolean useFastDataInput) {
mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
@@ -125,6 +127,7 @@
mBucketDuration = bucketDuration;
mOnlyTags = onlyTags;
mWipeOnError = wipeOnError;
+ mUseFastDataInput = useFastDataInput;
mPending = new NetworkStatsCollection(bucketDuration);
mSinceBoot = new NetworkStatsCollection(bucketDuration);
@@ -195,8 +198,12 @@
}
private NetworkStatsCollection loadLocked(long start, long end) {
- if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
- final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
+ if (LOGD) {
+ Log.d(TAG, "loadLocked() reading from disk for " + mCookie
+ + " useFastDataInput: " + mUseFastDataInput);
+ }
+ final NetworkStatsCollection res =
+ new NetworkStatsCollection(mBucketDuration, mUseFastDataInput);
try {
mRotator.readMatching(res, start, end);
res.recordCollection(mPending);
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 2c9f30c..ad83e7f 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);
+ wipeOnError, false /* useFastDataInput */);
}
@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 585157f..c3ea9f4 100644
--- a/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt
+++ b/tests/benchmark/src/android/net/netstats/benchmarktests/NetworkStatsTest.kt
@@ -133,7 +133,16 @@
}
@Test
- fun testReadFromRecorder_manyUids() {
+ fun testReadFromRecorder_manyUids_useDataInput() {
+ doTestReadFromRecorder_manyUids(useFastDataInput = false)
+ }
+
+ @Test
+ fun testReadFromRecorder_manyUids_useFastDataInput() {
+ doTestReadFromRecorder_manyUids(useFastDataInput = true)
+ }
+
+ fun doTestReadFromRecorder_manyUids(useFastDataInput: Boolean) {
val mockObserver = mock<NonMonotonicObserver<String>>()
val mockDropBox = mock<DropBoxManager>()
testFilesAssets.forEach {
@@ -146,7 +155,8 @@
PREFIX_UID,
UID_COLLECTION_BUCKET_DURATION_MS,
false /* includeTags */,
- false /* wipeOnError */
+ false /* wipeOnError */,
+ useFastDataInput /* useFastDataInput */
)
recorder.orLoadCompleteLocked
}
diff --git a/tests/unit/java/android/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index a6e9e95..81557f8 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -64,6 +64,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -90,7 +91,8 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public class NetworkStatsCollectionTest {
-
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
private static final String TEST_FILE = "test.bin";
private static final String TEST_IMSI = "310260000000000";
private static final int TEST_SUBID = 1;
@@ -199,6 +201,33 @@
77017831L, 100995L, 35436758L, 92344L);
}
+ private InputStream getUidInputStreamFromRes(int uidRes) throws Exception {
+ final File testFile =
+ new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE);
+ stageFile(uidRes, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyUid(testFile, true);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(bos);
+ return new ByteArrayInputStream(bos.toByteArray());
+ }
+
+ @Test
+ public void testFastDataInputRead() throws Exception {
+ final NetworkStatsCollection legacyCollection =
+ new NetworkStatsCollection(30 * MINUTE_IN_MILLIS, false /* useFastDataInput */);
+ final NetworkStatsCollection fastReadCollection =
+ new NetworkStatsCollection(30 * MINUTE_IN_MILLIS, true /* useFastDataInput */);
+ final InputStream bis = getUidInputStreamFromRes(R.raw.netstats_uid_v4);
+ legacyCollection.read(bis);
+ bis.reset();
+ fastReadCollection.read(bis);
+ assertCollectionEntries(legacyCollection.getEntries(), fastReadCollection);
+ }
+
@Test
public void testStartEndAtomicBuckets() throws Exception {
final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
diff --git a/tests/unit/java/android/net/NetworkStatsRecorderTest.java b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
index fad11a3..e8f853e 100644
--- a/tests/unit/java/android/net/NetworkStatsRecorderTest.java
+++ b/tests/unit/java/android/net/NetworkStatsRecorderTest.java
@@ -64,7 +64,8 @@
private NetworkStatsRecorder buildRecorder(FileRotator rotator, boolean wipeOnError) {
return new NetworkStatsRecorder(rotator, mObserver, mDropBox, TEST_PREFIX,
- HOUR_IN_MILLIS, false /* includeTags */, wipeOnError);
+ HOUR_IN_MILLIS, false /* includeTags */, wipeOnError,
+ false /* useFastDataInput */);
}
@Test
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 7a4dfed..aa4c34e 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -2230,7 +2230,8 @@
final DropBoxManager dropBox = mock(DropBoxManager.class);
return new NetworkStatsRecorder(new FileRotator(
directory, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
- observer, dropBox, prefix, config.bucketDuration, includeTags, wipeOnError);
+ observer, dropBox, prefix, config.bucketDuration, includeTags, wipeOnError,
+ false /* useFastDataInput */);
}
private NetworkStatsCollection getLegacyCollection(String prefix, boolean includeTags) {