From 6986e790c0f15e4432c3d68a95f3012ccf117ea1 Mon Sep 17 00:00:00 2001 From: Zhengyu Zhan Date: Wed, 6 Jan 2021 19:39:14 +0800 Subject: [PATCH] Support Dual ViLTE stats Some operator supports multiple VILTE, both two SIM cards can make VILTE call. We need to record VILTE data to different interface. Now NetworkStatsService has only one VILTE data interface: "vt_data0". All VILTE data stats will be recorded in this interface. Bug: 130871087 Test: NetworkStatsServiceTest#testDualVilteProviderStats Change-Id: I000ec756827bc42cac88dd0a2ca6b7999252357e --- .../server/net/NetworkStatsService.java | 23 ++++- .../server/net/NetworkStatsServiceTest.java | 86 +++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 7b376847fd447..de14831ec39e8 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -24,6 +24,7 @@ import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkIdentity.SUBTYPE_COMBINED; import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; @@ -65,6 +66,7 @@ import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -101,7 +103,9 @@ import android.net.NetworkStateSnapshot; import android.net.NetworkStats; import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; +import android.net.NetworkSpecifier; import android.net.NetworkTemplate; +import android.net.TelephonyNetworkSpecifier; import android.net.TrafficStats; import android.net.UnderlyingNetworkInfo; import android.net.Uri; @@ -1320,8 +1324,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), ident.getRoaming(), true /* metered */, true /* onDefaultNetwork */, ident.getOemManaged()); - findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); + final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot); + findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent); } if (isMobile) { @@ -1376,6 +1381,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]); } + private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) { + if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + throw new IllegalArgumentException("Mobile state need capability TRANSPORT_CELLULAR"); + } + + final NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier(); + if (spec instanceof TelephonyNetworkSpecifier) { + return ((TelephonyNetworkSpecifier) spec).getSubscriptionId(); + } else { + Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier"); + return INVALID_SUBSCRIPTION_ID; + } + } + /** * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 9334e2c4ad774..eeeb4fb1d9293 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -89,6 +89,7 @@ import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.UnderlyingNetworkInfo; +import android.net.TelephonyNetworkSpecifier; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.ConditionVariable; import android.os.Handler; @@ -1279,6 +1280,77 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { provider.assertNoCallback(); } + @Test + public void testDualVilteProviderStats() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + final int subId1 = 1; + final int subId2 = 2; + final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ + buildImsState(IMSI_1, subId1, TEST_IFACE), + buildImsState(IMSI_2, subId2, TEST_IFACE2)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Verifies that one requestStatsUpdate will be called during iface update. + provider.expectOnRequestStatsUpdate(0 /* unused */); + + // Create some initial traffic and report to the service. + incrementCurrentTime(HOUR_IN_MILLIS); + final String vtIface1 = NetworkStats.IFACE_VT + subId1; + final String vtIface2 = NetworkStats.IFACE_VT + subId2; + final NetworkStats expectedStats = new NetworkStats(0L, 1) + .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 128L, 2L, 128L, 2L, 1L)) + .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 64L, 1L, 64L, 1L, 1L)); + cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); + + // Make another empty mutable stats object. This is necessary since the new NetworkStats + // object will be used to compare with the old one in NetworkStatsRecoder, two of them + // cannot be the same object. + expectNetworkStatsUidDetail(buildEmptyStats()); + + forcePollAndWaitForIdle(); + + // Verifies that one requestStatsUpdate and setAlert will be called during polling. + provider.expectOnRequestStatsUpdate(0 /* unused */); + provider.expectOnSetAlert(MB_IN_BYTES); + + // Verifies that service recorded history, does not verify uid tag part. + assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1); + + // Verifies that onStatsUpdated updates the stats accordingly. + NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); + + stats = mSession.getSummaryForAllUid( + sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); + + // Verifies that unregister the callback will remove the provider from service. + cb.unregister(); + forcePollAndWaitForIdle(); + provider.assertNoCallback(); + } + @Test public void testStatsProviderSetAlert() throws Exception { // Pretend that network comes online. @@ -1616,6 +1688,20 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { TYPE_MOBILE); } + private static NetworkStateSnapshot buildImsState( + String subscriberId, int subId, String ifaceName) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(ifaceName); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)); + return new NetworkStateSnapshot( + MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); + } + private long getElapsedRealtime() { return mElapsedRealtime; }