Deduplicate items after clear interface in NetworkStats

Follow-up from commit Ie60829a65d0d9b5b63ad353695a820c0586e3665,
the interface field was cleared before returning the result to
the caller. However, this can cause problems in the
NetworkStats#subtract method. If the interface field is cleared,
the findIndexHinted method can match to a wrong entry.
This is because the keys of multiple entries will now be the
same. This can cause the subtract result to be unexpectedly
large and the return value of getUidStatsForTransport to be
mismatched with the values retrieved from other APIs.

Test: atest FrameworksNetTests:android.net.connectivity.com.android.server.net.NetworkStatsServiceTest \
      FrameworksNetTests:android.net.connectivity.android.net.NetworkStatsTest
Bug: 290728278
Change-Id: I891ab29b8a2902663febc7c32b04417caf510926
This commit is contained in:
Junyu Lai
2023-07-18 18:02:29 +08:00
parent 0b9c0835b9
commit 23d89c0bbf
5 changed files with 37 additions and 23 deletions

View File

@@ -41,6 +41,7 @@ import java.util.Arrays;
abstract class NetworkStatsBaseTest {
static final String TEST_IFACE = "test0";
static final String TEST_IFACE2 = "test1";
static final String TEST_IFACE3 = "test2";
static final String TUN_IFACE = "test_nss_tun0";
static final String TUN_IFACE2 = "test_nss_tun1";

View File

@@ -1250,8 +1250,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
TEST_IFACE2, IMSI_1, null /* wifiNetworkKey */,
false /* isTemporarilyNotMetered */, false /* isRoaming */);
final NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {
mobileState, buildWifiState()};
final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
mobileState, buildWifiState(false, TEST_IFACE, null),
buildWifiState(false, TEST_IFACE3, null)};
mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states),
new UnderlyingNetworkInfo[0]);
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE);
@@ -1266,16 +1267,22 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
final NetworkStats.Entry entry3 = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 512L, 4L, 2L);
// Add an entry that with different wifi interface, but expected to be merged into entry3
// after clearing interface information.
final NetworkStats.Entry entry4 = new NetworkStats.Entry(
TEST_IFACE3, UID_BLUE, SET_DEFAULT, 0xBEEF, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 5L);
final TetherStatsParcel[] emptyTetherStats = {};
// The interfaces that expect to be used to query the stats.
final String[] wifiIfaces = {TEST_IFACE};
final String[] wifiIfaces = {TEST_IFACE, TEST_IFACE3};
incrementCurrentTime(HOUR_IN_MILLIS);
mockDefaultSettings();
mockNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
mockNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
.insertEntry(entry1)
.insertEntry(entry2)
.insertEntry(entry3), emptyTetherStats, wifiIfaces);
.insertEntry(entry3)
.insertEntry(entry4), emptyTetherStats, wifiIfaces);
// getUidStatsForTransport (through getNetworkStatsUidDetail) adds all operation counts
// with active interface, and the interface here is mobile interface, so this test makes
@@ -1293,7 +1300,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
assertValues(wifiStats, null /* iface */, UID_RED, SET_DEFAULT, 0xF00D,
METERED_NO, ROAMING_NO, METERED_NO, 50L, 5L, 50L, 5L, 1L);
assertValues(wifiStats, null /* iface */, UID_BLUE, SET_DEFAULT, 0xBEEF,
METERED_NO, ROAMING_NO, METERED_NO, 1024L, 8L, 512L, 4L, 2L);
METERED_NO, ROAMING_NO, METERED_NO, 1025L, 10L, 515L, 8L, 7L);
final String[] mobileIfaces = {TEST_IFACE2};
mockNetworkStatsUidDetail(buildEmptyStats(), emptyTetherStats, mobileIfaces);