Merge "[AF01] Move NetworkStatsCollection comparison code to NetworkStatsCollection" into main

This commit is contained in:
Junyu Lai 2023-12-06 02:26:14 +00:00 • committed by Gerrit Code Review
commit 2f67e0f2d4
2 changed files with 97 additions and 88 deletions

View file

@ -26,6 +26,7 @@ import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
import static android.net.NetworkStats.SET_ALL;
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.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.MATCH_BLUETOOTH;
@ -50,6 +51,7 @@ import android.service.NetworkStatsCollectionStatsProto;
import android.telephony.SubscriptionPlan;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
@ -924,6 +926,100 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
private static String str(NetworkStatsCollection.Key key) {
StringBuilder sb = new StringBuilder()
.append(key.ident.toString())
.append(" uid=").append(key.uid);
if (key.set != SET_FOREGROUND) {
sb.append(" set=").append(key.set);
}
if (key.tag != 0) {
sb.append(" tag=").append(key.tag);
}
return sb.toString();
}
// The importer will modify some keys when importing them.
// In order to keep the comparison code simple, add such special cases here and simply
// ignore them. This should not impact fidelity much because the start/end checks and the total
// bytes check still need to pass.
private static boolean couldKeyChangeOnImport(NetworkStatsCollection.Key key) {
if (key.ident.isEmpty()) return false;
final NetworkIdentity firstIdent = key.ident.iterator().next();
// Non-mobile network with non-empty RAT type.
// This combination is invalid and the NetworkIdentity.Builder will throw if it is passed
// in, but it looks like it was previously possible to persist it to disk. The importer sets
// the RAT type to NETWORK_TYPE_ALL.
if (firstIdent.getType() != ConnectivityManager.TYPE_MOBILE
&& firstIdent.getRatType() != NetworkTemplate.NETWORK_TYPE_ALL) {
return true;
}
return false;
}
/**
* Compare two {@link NetworkStatsCollection} instances and returning a human-readable
* string description of difference for debugging purpose.
*
* @hide
*/
@Nullable
public static String compareStats(
NetworkStatsCollection migrated, NetworkStatsCollection legacy) {
final Map<NetworkStatsCollection.Key, NetworkStatsHistory> migEntries =
migrated.getEntries();
final Map<NetworkStatsCollection.Key, NetworkStatsHistory> legEntries = legacy.getEntries();
final ArraySet<NetworkStatsCollection.Key> unmatchedLegKeys =
new ArraySet<>(legEntries.keySet());
for (NetworkStatsCollection.Key legKey : legEntries.keySet()) {
final NetworkStatsHistory legHistory = legEntries.get(legKey);
final NetworkStatsHistory migHistory = migEntries.get(legKey);
if (migHistory == null && couldKeyChangeOnImport(legKey)) {
unmatchedLegKeys.remove(legKey);
continue;
}
if (migHistory == null) {
return "Missing migrated history for legacy key " + str(legKey)
+ ", legacy history was " + legHistory;
}
if (!migHistory.isSameAs(legHistory)) {
return "Difference in history for key " + legKey + "; legacy history " + legHistory
+ ", migrated history " + migHistory;
}
unmatchedLegKeys.remove(legKey);
}
if (!unmatchedLegKeys.isEmpty()) {
final NetworkStatsHistory first = legEntries.get(unmatchedLegKeys.valueAt(0));
return "Found unmatched legacy keys: count=" + unmatchedLegKeys.size()
+ ", first unmatched collection " + first;
}
if (migrated.getStartMillis() != legacy.getStartMillis()
|| migrated.getEndMillis() != legacy.getEndMillis()) {
return "Start / end of the collections "
+ migrated.getStartMillis() + "/" + legacy.getStartMillis() + " and "
+ migrated.getEndMillis() + "/" + legacy.getEndMillis()
+ " don't match";
}
if (migrated.getTotalBytes() != legacy.getTotalBytes()) {
return "Total bytes " + migrated.getTotalBytes() + " and " + legacy.getTotalBytes()
+ " don't match for collections with start/end "
+ migrated.getStartMillis()
+ "/" + legacy.getStartMillis();
}
return null;
}
/**
* the identifier that associate with the {@link NetworkStatsHistory} object to identify
* a certain record in the {@link NetworkStatsCollection} object.

View file

@ -44,6 +44,7 @@ import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsCollection.compareStats;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.MATCH_MOBILE;
import static android.net.NetworkTemplate.MATCH_TEST;
@ -95,7 +96,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetd;
import android.net.INetworkStatsService;
@ -1272,93 +1272,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return true;
}
private static String str(NetworkStatsCollection.Key key) {
StringBuilder sb = new StringBuilder()
.append(key.ident.toString())
.append(" uid=").append(key.uid);
if (key.set != SET_FOREGROUND) {
sb.append(" set=").append(key.set);
}
if (key.tag != 0) {
sb.append(" tag=").append(key.tag);
}
return sb.toString();
}
// The importer will modify some keys when importing them.
// In order to keep the comparison code simple, add such special cases here and simply
// ignore them. This should not impact fidelity much because the start/end checks and the total
// bytes check still need to pass.
private static boolean couldKeyChangeOnImport(NetworkStatsCollection.Key key) {
if (key.ident.isEmpty()) return false;
final NetworkIdentity firstIdent = key.ident.iterator().next();
// Non-mobile network with non-empty RAT type.
// This combination is invalid and the NetworkIdentity.Builder will throw if it is passed
// in, but it looks like it was previously possible to persist it to disk. The importer sets
// the RAT type to NETWORK_TYPE_ALL.
if (firstIdent.getType() != ConnectivityManager.TYPE_MOBILE
&& firstIdent.getRatType() != NetworkTemplate.NETWORK_TYPE_ALL) {
return true;
}
return false;
}
@Nullable
private static String compareStats(
NetworkStatsCollection migrated, NetworkStatsCollection legacy) {
final Map<NetworkStatsCollection.Key, NetworkStatsHistory> migEntries =
migrated.getEntries();
final Map<NetworkStatsCollection.Key, NetworkStatsHistory> legEntries = legacy.getEntries();
final ArraySet<NetworkStatsCollection.Key> unmatchedLegKeys =
new ArraySet<>(legEntries.keySet());
for (NetworkStatsCollection.Key legKey : legEntries.keySet()) {
final NetworkStatsHistory legHistory = legEntries.get(legKey);
final NetworkStatsHistory migHistory = migEntries.get(legKey);
if (migHistory == null && couldKeyChangeOnImport(legKey)) {
unmatchedLegKeys.remove(legKey);
continue;
}
if (migHistory == null) {
return "Missing migrated history for legacy key " + str(legKey)
+ ", legacy history was " + legHistory;
}
if (!migHistory.isSameAs(legHistory)) {
return "Difference in history for key " + legKey + "; legacy history " + legHistory
+ ", migrated history " + migHistory;
}
unmatchedLegKeys.remove(legKey);
}
if (!unmatchedLegKeys.isEmpty()) {
final NetworkStatsHistory first = legEntries.get(unmatchedLegKeys.valueAt(0));
return "Found unmatched legacy keys: count=" + unmatchedLegKeys.size()
+ ", first unmatched collection " + first;
}
if (migrated.getStartMillis() != legacy.getStartMillis()
|| migrated.getEndMillis() != legacy.getEndMillis()) {
return "Start / end of the collections "
+ migrated.getStartMillis() + "/" + legacy.getStartMillis() + " and "
+ migrated.getEndMillis() + "/" + legacy.getEndMillis()
+ " don't match";
}
if (migrated.getTotalBytes() != legacy.getTotalBytes()) {
return "Total bytes " + migrated.getTotalBytes() + " and " + legacy.getTotalBytes()
+ " don't match for collections with start/end "
+ migrated.getStartMillis()
+ "/" + legacy.getStartMillis();
}
return null;
}
@GuardedBy("mStatsLock")
@NonNull
private NetworkStatsCollection readPlatformCollectionForRecorder(