Snap for 8656240 from edda01a74467368c7e34883a7358e531c48e45bc to tm-release
Change-Id: I0f6ea54477dab785436c5843a5fd503d2a018031
diff --git a/Tethering/apex/manifest.json b/Tethering/apex/manifest.json
index 9c32cc8..3cb03ed 100644
--- a/Tethering/apex/manifest.json
+++ b/Tethering/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.tethering",
- "version": 330090000
+ "version": 339990000
}
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 0ff9d96..738e9cc 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -58,6 +58,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Random;
+import java.util.TreeMap;
/**
* Collection of historical network statistics, recorded into equally-sized
@@ -253,6 +254,28 @@
+ ", operations=" + operations
+ "}";
}
+
+ /**
+ * Add the given {@link Entry} with this instance and return a new {@link Entry}
+ * instance as the result.
+ *
+ * @hide
+ */
+ @NonNull
+ public Entry plus(@NonNull Entry another, long bucketDuration) {
+ if (this.bucketStart != another.bucketStart) {
+ throw new IllegalArgumentException("bucketStart " + this.bucketStart
+ + " is not equal to " + another.bucketStart);
+ }
+ return new Entry(this.bucketStart,
+ // Active time should not go over bucket duration.
+ Math.min(this.activeTime + another.activeTime, bucketDuration),
+ this.rxBytes + another.rxBytes,
+ this.rxPackets + another.rxPackets,
+ this.txBytes + another.txBytes,
+ this.txPackets + another.txPackets,
+ this.operations + another.operations);
+ }
}
/** @hide */
@@ -1109,14 +1132,8 @@
* Builder class for {@link NetworkStatsHistory}.
*/
public static final class Builder {
+ private final TreeMap<Long, Entry> mEntries;
private final long mBucketDuration;
- private final List<Long> mBucketStart;
- private final List<Long> mActiveTime;
- private final List<Long> mRxBytes;
- private final List<Long> mRxPackets;
- private final List<Long> mTxBytes;
- private final List<Long> mTxPackets;
- private final List<Long> mOperations;
/**
* Creates a new Builder with given bucket duration and initial capacity to construct
@@ -1127,66 +1144,31 @@
*/
public Builder(long bucketDuration, int initialCapacity) {
mBucketDuration = bucketDuration;
- mBucketStart = new ArrayList<>(initialCapacity);
- mActiveTime = new ArrayList<>(initialCapacity);
- mRxBytes = new ArrayList<>(initialCapacity);
- mRxPackets = new ArrayList<>(initialCapacity);
- mTxBytes = new ArrayList<>(initialCapacity);
- mTxPackets = new ArrayList<>(initialCapacity);
- mOperations = new ArrayList<>(initialCapacity);
- }
-
- private void addToElement(List<Long> list, int pos, long value) {
- list.set(pos, list.get(pos) + value);
+ // Create a collection that is always sorted and can deduplicate items by the timestamp.
+ mEntries = new TreeMap<>();
}
/**
- * Add an {@link Entry} into the {@link NetworkStatsHistory} instance.
+ * Add an {@link Entry} into the {@link NetworkStatsHistory} instance. If the timestamp
+ * already exists, the given {@link Entry} will be combined into existing entry.
*
- * @param entry The target {@link Entry} object. The entry timestamp must be greater than
- * that of any previously-added entry.
+ * @param entry The target {@link Entry} object.
* @return The builder object.
*/
@NonNull
public Builder addEntry(@NonNull Entry entry) {
- final int lastBucket = mBucketStart.size() - 1;
- final long lastBucketStart = (lastBucket != -1) ? mBucketStart.get(lastBucket) : 0;
-
- // If last bucket has the same timestamp, modify it instead of adding another bucket.
- // This allows callers to pass in the same bucket twice (e.g., to accumulate
- // data over time), but still requires that entries must be sorted.
- // The importer will do this in case a rotated file has the same timestamp as
- // the previous file.
- if (lastBucket != -1 && entry.bucketStart == lastBucketStart) {
- addToElement(mActiveTime, lastBucket, entry.activeTime);
- addToElement(mRxBytes, lastBucket, entry.rxBytes);
- addToElement(mRxPackets, lastBucket, entry.rxPackets);
- addToElement(mTxBytes, lastBucket, entry.txBytes);
- addToElement(mTxPackets, lastBucket, entry.txPackets);
- addToElement(mOperations, lastBucket, entry.operations);
- return this;
+ final Entry existing = mEntries.get(entry.bucketStart);
+ if (existing != null) {
+ mEntries.put(entry.bucketStart, existing.plus(entry, mBucketDuration));
+ } else {
+ mEntries.put(entry.bucketStart, entry);
}
-
- // Inserting in the middle is prohibited for performance reasons.
- if (entry.bucketStart <= lastBucketStart) {
- throw new IllegalArgumentException("new bucket start " + entry.bucketStart
- + " must be greater than last bucket start " + lastBucketStart);
- }
-
- // Common case: add entries at the end of the list.
- mBucketStart.add(entry.bucketStart);
- mActiveTime.add(entry.activeTime);
- mRxBytes.add(entry.rxBytes);
- mRxPackets.add(entry.rxPackets);
- mTxBytes.add(entry.txBytes);
- mTxPackets.add(entry.txPackets);
- mOperations.add(entry.operations);
return this;
}
- private static long sum(@NonNull List<Long> list) {
- long sum = 0;
- for (long entry : list) {
+ private static long sum(@NonNull long[] array) {
+ long sum = 0L;
+ for (long entry : array) {
sum += entry;
}
return sum;
@@ -1199,16 +1181,30 @@
*/
@NonNull
public NetworkStatsHistory build() {
- return new NetworkStatsHistory(mBucketDuration,
- CollectionUtils.toLongArray(mBucketStart),
- CollectionUtils.toLongArray(mActiveTime),
- CollectionUtils.toLongArray(mRxBytes),
- CollectionUtils.toLongArray(mRxPackets),
- CollectionUtils.toLongArray(mTxBytes),
- CollectionUtils.toLongArray(mTxPackets),
- CollectionUtils.toLongArray(mOperations),
- mBucketStart.size(),
- sum(mRxBytes) + sum(mTxBytes));
+ int size = mEntries.size();
+ final long[] bucketStart = new long[size];
+ final long[] activeTime = new long[size];
+ final long[] rxBytes = new long[size];
+ final long[] rxPackets = new long[size];
+ final long[] txBytes = new long[size];
+ final long[] txPackets = new long[size];
+ final long[] operations = new long[size];
+
+ int i = 0;
+ for (Entry entry : mEntries.values()) {
+ bucketStart[i] = entry.bucketStart;
+ activeTime[i] = entry.activeTime;
+ rxBytes[i] = entry.rxBytes;
+ rxPackets[i] = entry.rxPackets;
+ txBytes[i] = entry.txBytes;
+ txPackets[i] = entry.txPackets;
+ operations[i] = entry.operations;
+ i++;
+ }
+
+ return new NetworkStatsHistory(mBucketDuration, bucketStart, activeTime,
+ rxBytes, rxPackets, txBytes, txPackets, operations,
+ size, sum(rxBytes) + sum(txBytes));
}
}
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index dc09bc2..64e56a1 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -958,8 +958,17 @@
// First, read all legacy collections. This is OEM code and it can throw. Don't
// commit any data to disk until all are read.
for (int i = 0; i < migrations.length; i++) {
+ String errMsg = null;
+ Throwable exception = null;
final MigrationInfo migration = migrations[i];
- migration.collection = readPlatformCollectionForRecorder(migration.recorder);
+
+ // Read the collection from platform code, and using fallback method if throws.
+ try {
+ migration.collection = readPlatformCollectionForRecorder(migration.recorder);
+ } catch (Throwable e) {
+ errMsg = "Failed to read stats from platform";
+ exception = e;
+ }
// Also read the collection with legacy method
final NetworkStatsRecorder legacyRecorder = legacyRecorders[i];
@@ -968,18 +977,22 @@
try {
legacyStats = legacyRecorder.getOrLoadCompleteLocked();
} catch (Throwable e) {
- Log.wtf(TAG, "Failed to read stats with legacy method", e);
- // Newer stats will be used here; that's the only thing that is usable
- continue;
+ Log.wtf(TAG, "Failed to read stats with legacy method for recorder " + i, e);
+ if (exception != null) {
+ throw exception;
+ } else {
+ // Use newer stats, since that's all that is available
+ continue;
+ }
}
- String errMsg;
- Throwable exception = null;
- try {
- errMsg = compareStats(migration.collection, legacyStats);
- } catch (Throwable e) {
- errMsg = "Failed to compare migrated stats with all stats";
- exception = e;
+ if (errMsg == null) {
+ try {
+ errMsg = compareStats(migration.collection, legacyStats);
+ } catch (Throwable e) {
+ errMsg = "Failed to compare migrated stats with all stats";
+ exception = e;
+ }
}
if (errMsg != null) {
diff --git a/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
index f8e041a..9343ea1 100644
--- a/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
+++ b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
@@ -22,12 +22,12 @@
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.SC_V2
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
@ConnectivityModuleTest
@RunWith(JUnit4::class)
@@ -37,6 +37,7 @@
@JvmField
val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+ @Ignore
@Test
fun testBuilder() {
val entry1 = NetworkStatsHistory.Entry(10, 30, 40, 4, 50, 5, 60)
@@ -53,21 +54,37 @@
statsSingle.assertEntriesEqual(entry1)
assertEquals(DateUtils.HOUR_IN_MILLIS, statsSingle.bucketDuration)
- // Verify the builder throws if the timestamp of added entry is not greater than
- // that of any previously-added entry.
- assertFailsWith(IllegalArgumentException::class) {
- NetworkStatsHistory
- .Builder(DateUtils.SECOND_IN_MILLIS, /* initialCapacity */ 0)
- .addEntry(entry1).addEntry(entry2).addEntry(entry3)
- .build()
- }
+ val statsMultiple = NetworkStatsHistory
+ .Builder(DateUtils.SECOND_IN_MILLIS, /* initialCapacity */ 0)
+ .addEntry(entry1).addEntry(entry2).addEntry(entry3)
+ .build()
+ assertEquals(DateUtils.SECOND_IN_MILLIS, statsMultiple.bucketDuration)
+ // Verify the entries exist and sorted.
+ statsMultiple.assertEntriesEqual(entry3, entry1, entry2)
+ }
+
+ @Ignore
+ @Test
+ fun testBuilderSortAndDeduplicate() {
+ val entry1 = NetworkStatsHistory.Entry(10, 30, 40, 4, 50, 5, 60)
+ val entry2 = NetworkStatsHistory.Entry(30, 15, 3, 41, 7, 1, 0)
+ val entry3 = NetworkStatsHistory.Entry(30, 999, 11, 14, 31, 2, 80)
+ val entry4 = NetworkStatsHistory.Entry(10, 15, 1, 17, 5, 33, 10)
+ val entry5 = NetworkStatsHistory.Entry(6, 1, 9, 11, 29, 1, 7)
+
+ // Entries for verification.
+ // Note that active time of 2 + 3 is truncated to bucket duration since the active time
+ // should not go over bucket duration.
+ val entry2and3 = NetworkStatsHistory.Entry(30, 1000, 14, 55, 38, 3, 80)
+ val entry1and4 = NetworkStatsHistory.Entry(10, 45, 41, 21, 55, 38, 70)
val statsMultiple = NetworkStatsHistory
.Builder(DateUtils.SECOND_IN_MILLIS, /* initialCapacity */ 0)
- .addEntry(entry3).addEntry(entry1).addEntry(entry2)
- .build()
+ .addEntry(entry1).addEntry(entry2).addEntry(entry3)
+ .addEntry(entry4).addEntry(entry5).build()
assertEquals(DateUtils.SECOND_IN_MILLIS, statsMultiple.bucketDuration)
- statsMultiple.assertEntriesEqual(entry3, entry1, entry2)
+ // Verify the entries sorted and deduplicated.
+ statsMultiple.assertEntriesEqual(entry5, entry1and4, entry2and3)
}
fun NetworkStatsHistory.assertEntriesEqual(vararg entries: NetworkStatsHistory.Entry) {
diff --git a/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java b/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java
new file mode 100644
index 0000000..c8ee0c7
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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 android.net.cts;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.NonNull;
+import android.net.EthernetNetworkUpdateRequest;
+import android.net.IpConfiguration;
+import android.net.NetworkCapabilities;
+import android.net.StaticIpConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+@RunWith(DevSdkIgnoreRunner.class)
+public class EthernetNetworkUpdateRequestTest {
+ private static final NetworkCapabilities DEFAULT_CAPS =
+ new NetworkCapabilities.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED).build();
+ private static final StaticIpConfiguration DEFAULT_STATIC_IP_CONFIG =
+ new StaticIpConfiguration.Builder().setDomains("test").build();
+ private static final IpConfiguration DEFAULT_IP_CONFIG =
+ new IpConfiguration.Builder()
+ .setStaticIpConfiguration(DEFAULT_STATIC_IP_CONFIG).build();
+
+ private EthernetNetworkUpdateRequest createRequest(@NonNull final NetworkCapabilities nc,
+ @NonNull final IpConfiguration ipConfig) {
+ return new EthernetNetworkUpdateRequest.Builder()
+ .setNetworkCapabilities(nc)
+ .setIpConfiguration(ipConfig)
+ .build();
+ }
+
+ @Test
+ public void testGetNetworkCapabilities() {
+ final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+ assertEquals(DEFAULT_CAPS, r.getNetworkCapabilities());
+ }
+
+ @Test
+ public void testGetIpConfiguration() {
+ final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+ assertEquals(DEFAULT_IP_CONFIG, r.getIpConfiguration());
+ }
+
+ @Test
+ public void testBuilderWithRequest() {
+ final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+ final EthernetNetworkUpdateRequest rFromExisting =
+ new EthernetNetworkUpdateRequest.Builder(r).build();
+
+ assertNotSame(r, rFromExisting);
+ assertEquals(r.getIpConfiguration(), rFromExisting.getIpConfiguration());
+ assertEquals(r.getNetworkCapabilities(), rFromExisting.getNetworkCapabilities());
+ }
+
+ @Test
+ public void testNullIpConfigurationAndNetworkCapabilitiesThrows() {
+ assertThrows("Should not be able to build with null ip config and network capabilities.",
+ IllegalStateException.class,
+ () -> createRequest(null, null));
+ }
+}