diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index c05138f8eae11..6a00d06440b97 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -98,8 +98,6 @@ import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkTemplate.MATCH_CARRIER; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; -import static android.net.NetworkTemplate.buildTemplateCarrierMetered; -import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED; @@ -168,7 +166,6 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; @@ -1344,7 +1341,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** - * Check {@link NetworkPolicy} against current {@link INetworkStatsService} + * Check {@link NetworkPolicy} against current {@link NetworkStatsManager} * to show visible notifications as needed. */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -2341,6 +2338,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return policy; } + /** + * Template to match all metered carrier networks with the given IMSI. + * + * @hide + */ + public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) { + Objects.requireNonNull(subscriberId); + return new NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(Set.of(subscriberId)) + .setMeteredness(METERED_YES).build(); + } + /** * Update the given {@link NetworkPolicy} based on any carrier-provided * defaults via {@link SubscriptionPlan} or {@link CarrierConfigManager}. @@ -2744,7 +2753,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { out.startTag(null, TAG_NETWORK_POLICY); writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule()); - final String subscriberId = template.getSubscriberId(); + final String subscriberId = template.getSubscriberIds().isEmpty() ? null + : template.getSubscriberIds().iterator().next(); if (subscriberId != null) { out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId); } @@ -3121,7 +3131,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // When two normalized templates conflict, prefer the most // restrictive policy - policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds); + policy.template = normalizeTemplate(policy.template, mMergedSubscriberIds); final NetworkPolicy existing = mNetworkPolicy.get(policy.template); if (existing == null || existing.compareTo(policy) > 0) { if (existing != null) { @@ -3132,6 +3142,46 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Examine the given template and normalize it. + * We pick the "lowest" merged subscriber as the primary + * for key purposes, and expand the template to match all other merged + * subscribers. + * + * There can be multiple merged subscriberIds for multi-SIM devices. + * + *

+ * For example, given an incoming template matching B, and the currently + * active merge set [A,B], we'd return a new template that primarily matches + * A, but also matches B. + */ + private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template, + @NonNull List mergedList) { + // Now there are several types of network which uses Subscriber Id to store network + // information. For instance: + // 1. A merged carrier wifi network which has TYPE_WIFI with a Subscriber Id. + // 2. A typical cellular network could have TYPE_MOBILE with a Subscriber Id. + + if (template.getSubscriberIds().isEmpty()) return template; + + for (final String[] merged : mergedList) { + // TODO: Handle incompatible subscriberIds if that happens in practice. + for (final String subscriberId : template.getSubscriberIds()) { + if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) { + // Requested template subscriber is part of the merged group; return + // a template that matches all merged subscribers. + return new NetworkTemplate.Builder(template.getMatchRule()) + .setWifiNetworkKeys(template.getWifiNetworkKeys()) + .setSubscriberIds(Set.of(merged)) + .setMeteredness(template.getMeteredness()) + .build(); + } + } + } + + return template; + } + @Override public void snoozeLimit(NetworkTemplate template) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); @@ -5583,7 +5633,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName()); NetworkTemplate templateCarrier = subscriber != null ? buildTemplateCarrierMetered(subscriber) : null; - NetworkTemplate templateMobile = buildTemplateMobileAll(subscriber); + NetworkTemplate templateMobile = subscriber != null + ? new NetworkTemplate.Builder(MATCH_MOBILE) + .setSubscriberIds(Set.of(subscriber)) + .setMeteredness(android.net.NetworkStats.METERED_YES) + .build() : null; for (NetworkPolicy policy : policies) { // All policies loaded from disk will be carrier templates, and setting will also only // set carrier templates, but we clear mobile templates just in case one is set by diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index e80721a10e902..94cf20f9c15be 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -51,10 +51,9 @@ import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkTemplate.MATCH_CARRIER; import static android.net.NetworkTemplate.MATCH_MOBILE; -import static android.net.NetworkTemplate.buildTemplateCarrierMetered; -import static android.net.NetworkTemplate.buildTemplateWifi; -import static android.net.TrafficStats.MB_IN_BYTES; +import static android.net.NetworkTemplate.MATCH_WIFI; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; @@ -205,6 +204,7 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -229,10 +229,12 @@ public class NetworkPolicyManagerServiceTest { private static final int TEST_SUB_ID = 42; private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS); - - private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY); + private static NetworkTemplate sTemplateWifi = new NetworkTemplate.Builder(MATCH_WIFI) + .setWifiNetworkKeys(Set.of(TEST_WIFI_NETWORK_KEY)).build(); private static NetworkTemplate sTemplateCarrierMetered = - buildTemplateCarrierMetered(TEST_IMSI); + new NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(Set.of(TEST_IMSI)) + .setMeteredness(METERED_YES).build(); /** * Path on assets where files used by {@link NetPolicyXml} are located. @@ -1160,11 +1162,12 @@ public class NetworkPolicyManagerServiceTest { mPolicyListener.expect().onMeteredIfacesChanged(any()); setNetworkPolicies(new NetworkPolicy( - sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false)); + sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, DataUnit.MEBIBYTES.toBytes(1), + DataUnit.MEBIBYTES.toBytes(2), false)); mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE})); verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, - (2 * MB_IN_BYTES) - 512); + DataUnit.MEBIBYTES.toBytes(2) - 512); } @Test @@ -1252,7 +1255,7 @@ public class NetworkPolicyManagerServiceTest { reset(mTelephonyManager, mNetworkManager, mNotifManager); TelephonyManager tmSub = expectMobileDefaults(); - mService.snoozeLimit(NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI)); + mService.snoozeLimit(sTemplateCarrierMetered); mService.updateNetworks(); verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true); @@ -1955,7 +1958,7 @@ public class NetworkPolicyManagerServiceTest { assertEquals("Unexpected number of network policies", 1, policies.length); NetworkPolicy actualPolicy = policies[0]; assertEquals("Unexpected template match rule in network policies", - NetworkTemplate.MATCH_WIFI, + MATCH_WIFI, actualPolicy.template.getMatchRule()); assertEquals("Unexpected subscriberIds size in network policies", actualPolicy.template.getSubscriberIds().size(), 0); @@ -2026,7 +2029,10 @@ public class NetworkPolicyManagerServiceTest { private static NetworkPolicy buildFakeCarrierPolicy(int cycleDay, long warningBytes, long limitBytes, boolean inferred) { - final NetworkTemplate template = buildTemplateCarrierMetered(FAKE_SUBSCRIBER_ID); + // TODO: Refactor this to use sTemplateCarrierMetered. + final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(Set.of(FAKE_SUBSCRIBER_ID)) + .setMeteredness(METERED_YES).build(); return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); }