Merge changes I1707d28e,Ibb4a2916 into main
* changes: Dump top distinct tag counts per uid Support per uid tag throttling to NetworkStats
This commit is contained in:
commit
04271629cc
3 changed files with 215 additions and 3 deletions
|
|
@ -19,6 +19,7 @@ package com.android.server.net;
|
|||
import static android.net.NetworkStats.INTERFACES_ALL;
|
||||
import static android.net.NetworkStats.TAG_ALL;
|
||||
import static android.net.NetworkStats.UID_ALL;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
|
|
@ -26,15 +27,26 @@ import android.net.NetworkStats;
|
|||
import android.net.UnderlyingNetworkInfo;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.net.module.util.DeviceConfigUtils;
|
||||
import com.android.server.BpfNetMaps;
|
||||
import com.android.server.connectivity.InterfaceTracker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
|
@ -65,6 +77,18 @@ public class NetworkStatsFactory {
|
|||
/** Set containing info about active VPNs and their underlying networks. */
|
||||
private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
|
||||
|
||||
static final String CONFIG_PER_UID_TAG_THROTTLING = "per_uid_tag_throttling";
|
||||
static final String CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD =
|
||||
"per_uid_tag_throttling_threshold";
|
||||
private static final int DEFAULT_TAGS_PER_UID_THRESHOLD = 1000;
|
||||
private static final int DUMP_TAGS_PER_UID_COUNT = 20;
|
||||
private final boolean mSupportPerUidTagThrottling;
|
||||
private final int mPerUidTagThrottlingThreshold;
|
||||
|
||||
// Map for set of distinct tags per uid. Used for tag count limiting.
|
||||
@GuardedBy("mPersistentDataLock")
|
||||
private final SparseArray<SparseBooleanArray> mUidTagSets = new SparseArray<>();
|
||||
|
||||
// A persistent snapshot of cumulative stats since device start
|
||||
@GuardedBy("mPersistentDataLock")
|
||||
private NetworkStats mPersistSnapshot;
|
||||
|
|
@ -110,6 +134,26 @@ public class NetworkStatsFactory {
|
|||
public BpfNetMaps createBpfNetMaps(@NonNull Context ctx) {
|
||||
return new BpfNetMaps(ctx, new InterfaceTracker(ctx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether one specific feature is not disabled.
|
||||
* @param name Flag name of the experiment in the tethering namespace.
|
||||
* @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut(Context, String)
|
||||
*/
|
||||
public boolean isFeatureNotChickenedOut(@NonNull Context context, @NonNull String name) {
|
||||
return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method for DeviceConfigUtils#getDeviceConfigPropertyInt for test injections.
|
||||
*
|
||||
* See {@link DeviceConfigUtils#getDeviceConfigPropertyInt(String, String, int)}
|
||||
* for more detailed information.
|
||||
*/
|
||||
public int getDeviceConfigPropertyInt(@NonNull String name, int defaultValue) {
|
||||
return DeviceConfigUtils.getDeviceConfigPropertyInt(
|
||||
NAMESPACE_TETHERING, name, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,6 +206,10 @@ public class NetworkStatsFactory {
|
|||
}
|
||||
mContext = ctx;
|
||||
mDeps = deps;
|
||||
mSupportPerUidTagThrottling = mDeps.isFeatureNotChickenedOut(
|
||||
ctx, CONFIG_PER_UID_TAG_THROTTLING);
|
||||
mPerUidTagThrottlingThreshold = mDeps.getDeviceConfigPropertyInt(
|
||||
CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD, DEFAULT_TAGS_PER_UID_THRESHOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -210,10 +258,13 @@ public class NetworkStatsFactory {
|
|||
requestSwapActiveStatsMapLocked();
|
||||
// Stats are always read from the inactive map, so they must be read after the
|
||||
// swap
|
||||
final NetworkStats stats = mDeps.getNetworkStatsDetail();
|
||||
final NetworkStats diff = mDeps.getNetworkStatsDetail();
|
||||
// Filter based on UID tag set before merging.
|
||||
final NetworkStats filteredDiff = mSupportPerUidTagThrottling
|
||||
? filterStatsByUidTagSets(diff) : diff;
|
||||
// BPF stats are incremental; fold into mPersistSnapshot.
|
||||
mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
|
||||
mPersistSnapshot.combineAllValues(stats);
|
||||
mPersistSnapshot.setElapsedRealtime(diff.getElapsedRealtime());
|
||||
mPersistSnapshot.combineAllValues(filteredDiff);
|
||||
|
||||
NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
|
||||
|
||||
|
|
@ -223,6 +274,41 @@ public class NetworkStatsFactory {
|
|||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mPersistentDataLock")
|
||||
private NetworkStats filterStatsByUidTagSets(NetworkStats stats) {
|
||||
final NetworkStats filteredStats =
|
||||
new NetworkStats(stats.getElapsedRealtime(), stats.size());
|
||||
|
||||
final NetworkStats.Entry entry = new NetworkStats.Entry();
|
||||
final Set<Integer> tooManyTagsUidSet = new ArraySet<>();
|
||||
for (int i = 0; i < stats.size(); i++) {
|
||||
stats.getValues(i, entry);
|
||||
final int uid = entry.uid;
|
||||
final int tag = entry.tag;
|
||||
|
||||
if (tag == NetworkStats.TAG_NONE) {
|
||||
filteredStats.combineValues(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
SparseBooleanArray tagSet = mUidTagSets.get(uid);
|
||||
if (tagSet == null) {
|
||||
tagSet = new SparseBooleanArray();
|
||||
}
|
||||
if (tagSet.size() < mPerUidTagThrottlingThreshold || tagSet.get(tag)) {
|
||||
filteredStats.combineValues(entry);
|
||||
tagSet.put(tag, true);
|
||||
mUidTagSets.put(uid, tagSet);
|
||||
} else {
|
||||
tooManyTagsUidSet.add(uid);
|
||||
}
|
||||
}
|
||||
if (tooManyTagsUidSet.size() > 0) {
|
||||
Log.wtf(TAG, "Too many tags detected for uids: " + tooManyTagsUidSet);
|
||||
}
|
||||
return filteredStats;
|
||||
}
|
||||
|
||||
@GuardedBy("mPersistentDataLock")
|
||||
private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
|
||||
NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
|
||||
|
|
@ -307,4 +393,34 @@ public class NetworkStatsFactory {
|
|||
pe.initCause(cause);
|
||||
return pe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the contents of NetworkStatsFactory.
|
||||
*/
|
||||
public void dump(IndentingPrintWriter pw) {
|
||||
dumpUidTagSets(pw);
|
||||
}
|
||||
|
||||
private void dumpUidTagSets(IndentingPrintWriter pw) {
|
||||
pw.println("Top distinct tag counts in UidTagSets:");
|
||||
pw.increaseIndent();
|
||||
final List<Pair<Integer, Integer>> countForUidList = new ArrayList<>();
|
||||
synchronized (mPersistentDataLock) {
|
||||
for (int i = 0; i < mUidTagSets.size(); i++) {
|
||||
final Pair<Integer, Integer> countForUid =
|
||||
new Pair<>(mUidTagSets.keyAt(i), mUidTagSets.valueAt(i).size());
|
||||
countForUidList.add(countForUid);
|
||||
}
|
||||
}
|
||||
Collections.sort(countForUidList,
|
||||
(entry1, entry2) -> Integer.compare(entry2.second, entry1.second));
|
||||
final int dumpSize = Math.min(countForUidList.size(), DUMP_TAGS_PER_UID_COUNT);
|
||||
for (int j = 0; j < dumpSize; j++) {
|
||||
final Pair<Integer, Integer> entry = countForUidList.get(j);
|
||||
pw.print(entry.first);
|
||||
pw.print("=");
|
||||
pw.println(entry.second);
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3228,6 +3228,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||
pw.increaseIndent();
|
||||
mSkDestroyListener.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
|
||||
pw.println();
|
||||
pw.println("NetworkStatsFactory logs:");
|
||||
pw.increaseIndent();
|
||||
mStatsFactory.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
|
|||
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
|
||||
import static android.net.NetworkStats.METERED_ALL;
|
||||
import static android.net.NetworkStats.METERED_NO;
|
||||
import static android.net.NetworkStats.METERED_YES;
|
||||
import static android.net.NetworkStats.ROAMING_ALL;
|
||||
import static android.net.NetworkStats.ROAMING_NO;
|
||||
import static android.net.NetworkStats.SET_ALL;
|
||||
|
|
@ -29,6 +30,8 @@ import static android.net.NetworkStats.TAG_ALL;
|
|||
import static android.net.NetworkStats.TAG_NONE;
|
||||
import static android.net.NetworkStats.UID_ALL;
|
||||
|
||||
import static com.android.server.net.NetworkStatsFactory.CONFIG_PER_UID_TAG_THROTTLING;
|
||||
import static com.android.server.net.NetworkStatsFactory.CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD;
|
||||
import static com.android.server.net.NetworkStatsFactory.kernelToTag;
|
||||
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
||||
|
||||
|
|
@ -36,6 +39,9 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -52,12 +58,15 @@ import com.android.internal.util.ProcFileReader;
|
|||
import com.android.server.BpfNetMaps;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRunner;
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule.FeatureFlag;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.testing.io.TestIoUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
|
@ -66,6 +75,7 @@ import org.mockito.MockitoAnnotations;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Tests for {@link NetworkStatsFactory}. */
|
||||
@RunWith(DevSdkIgnoreRunner.class)
|
||||
|
|
@ -73,6 +83,7 @@ import java.net.ProtocolException;
|
|||
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
|
||||
public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
|
||||
private static final String CLAT_PREFIX = "v4-";
|
||||
private static final int TEST_TAGS_PER_UID_THRESHOLD = 10;
|
||||
|
||||
private File mTestProc;
|
||||
private NetworkStatsFactory mFactory;
|
||||
|
|
@ -80,6 +91,16 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
|
|||
@Mock private NetworkStatsFactory.Dependencies mDeps;
|
||||
@Mock private BpfNetMaps mBpfNetMaps;
|
||||
|
||||
final HashMap<String, Boolean> mFeatureFlags = new HashMap<>();
|
||||
// This will set feature flags from @FeatureFlag annotations
|
||||
// into the map before setUp() runs.
|
||||
@Rule
|
||||
public final SetFeatureFlagsRule mSetFeatureFlagsRule =
|
||||
new SetFeatureFlagsRule((name, enabled) -> {
|
||||
mFeatureFlags.put(name, enabled);
|
||||
return null;
|
||||
}, (name) -> mFeatureFlags.getOrDefault(name, false));
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
|
@ -90,6 +111,10 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
|
|||
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
|
||||
System.loadLibrary("networkstatsfactorytestjni");
|
||||
doReturn(mBpfNetMaps).when(mDeps).createBpfNetMaps(any());
|
||||
doAnswer(invocation -> mFeatureFlags.getOrDefault((String) invocation.getArgument(1), true))
|
||||
.when(mDeps).isFeatureNotChickenedOut(any(), anyString());
|
||||
doReturn(TEST_TAGS_PER_UID_THRESHOLD).when(mDeps)
|
||||
.getDeviceConfigPropertyInt(eq(CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD), anyInt());
|
||||
|
||||
mFactory = new NetworkStatsFactory(mContext, mDeps);
|
||||
mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
|
||||
|
|
@ -498,6 +523,71 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
|
|||
assertValues(removedUidsStats, TEST_IFACE, UID_GREEN, 64L, 3L, 1024L, 8L);
|
||||
}
|
||||
|
||||
@FeatureFlag(name = CONFIG_PER_UID_TAG_THROTTLING)
|
||||
@Test
|
||||
public void testFilterTooManyTags_featureEnabled() throws Exception {
|
||||
doTestFilterTooManyTags(true);
|
||||
}
|
||||
|
||||
@FeatureFlag(name = CONFIG_PER_UID_TAG_THROTTLING, enabled = false)
|
||||
@Test
|
||||
public void testFilterTooManyTags_featureDisabled() throws Exception {
|
||||
doTestFilterTooManyTags(false);
|
||||
}
|
||||
|
||||
private void doTestFilterTooManyTags(boolean supportPerUidTagThrottling) throws Exception {
|
||||
// Add entries for UID_RED which reaches the threshold.
|
||||
final NetworkStats statsWithManyTags = new NetworkStats(0L, TEST_TAGS_PER_UID_THRESHOLD);
|
||||
for (int tag = 1; tag <= TEST_TAGS_PER_UID_THRESHOLD; tag++) {
|
||||
statsWithManyTags.combineValues(
|
||||
new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, tag,
|
||||
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L));
|
||||
}
|
||||
doReturn(statsWithManyTags).when(mDeps).getNetworkStatsDetail();
|
||||
final NetworkStats stats1 = mFactory.readNetworkStatsDetail();
|
||||
assertEquals(stats1.size(), TEST_TAGS_PER_UID_THRESHOLD);
|
||||
|
||||
// Add 2 new entries with pre-existing tag, verify they can be added no matter what.
|
||||
final NetworkStats newDiffWithExistingTag = new NetworkStats(0L, 2);
|
||||
// This one should be added as a new entry, as the metered data doesn't exist yet.
|
||||
newDiffWithExistingTag.combineValues(
|
||||
new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
|
||||
TEST_TAGS_PER_UID_THRESHOLD,
|
||||
METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 3L, 5L, 8L, 1L, 1L));
|
||||
// This one should be combined into existing entry.
|
||||
newDiffWithExistingTag.combineValues(
|
||||
new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
|
||||
TEST_TAGS_PER_UID_THRESHOLD,
|
||||
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 5L));
|
||||
|
||||
doReturn(newDiffWithExistingTag).when(mDeps).getNetworkStatsDetail();
|
||||
final NetworkStats stats2 = mFactory.readNetworkStatsDetail();
|
||||
assertEquals(stats2.size(), TEST_TAGS_PER_UID_THRESHOLD + 1);
|
||||
assertValues(stats2, TEST_IFACE, UID_RED, SET_DEFAULT, TEST_TAGS_PER_UID_THRESHOLD,
|
||||
METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 3L, 5L, 8L, 1L, 1L);
|
||||
assertValues(stats2, TEST_IFACE, UID_RED, SET_DEFAULT, TEST_TAGS_PER_UID_THRESHOLD,
|
||||
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13L, 20L, 17L, 5L, 5L);
|
||||
|
||||
// Add an entry which exceeds the threshold, verify the entry is filtered out.
|
||||
final NetworkStats newDiffWithNonExistingTag = new NetworkStats(0L, 1);
|
||||
newDiffWithNonExistingTag.combineValues(
|
||||
new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
|
||||
TEST_TAGS_PER_UID_THRESHOLD + 1,
|
||||
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L));
|
||||
doReturn(newDiffWithNonExistingTag).when(mDeps).getNetworkStatsDetail();
|
||||
final NetworkStats stats3 = mFactory.readNetworkStatsDetail();
|
||||
if (supportPerUidTagThrottling) {
|
||||
assertEquals(stats3.size(), TEST_TAGS_PER_UID_THRESHOLD + 1);
|
||||
assertNoStatsEntry(stats3, TEST_IFACE, UID_RED, SET_DEFAULT,
|
||||
TEST_TAGS_PER_UID_THRESHOLD + 1);
|
||||
} else {
|
||||
assertEquals(stats3.size(), TEST_TAGS_PER_UID_THRESHOLD + 2);
|
||||
assertValues(stats3, TEST_IFACE, UID_RED, SET_DEFAULT,
|
||||
TEST_TAGS_PER_UID_THRESHOLD + 1,
|
||||
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkStats buildEmptyStats() {
|
||||
return new NetworkStats(SystemClock.elapsedRealtime(), 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue