Loading core/java/android/provider/Settings.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -8930,6 +8930,20 @@ public final class Settings { /** {@hide} */ /** {@hide} */ public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; /** {@hide} */ public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled"; /** {@hide} */ public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited"; /** {@hide} */ public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited"; /** {@hide} */ public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs"; /** {@hide} */ public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath"; /** {@hide} */ public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled"; /** /** * User preference for which network(s) should be used. Only the * User preference for which network(s) should be used. Only the * connectivity service should touch this. * connectivity service should touch this. Loading core/tests/coretests/src/android/provider/SettingsBackupTest.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -302,6 +302,12 @@ public class SettingsBackupTest { Settings.Global.NETSTATS_UID_TAG_DELETE_AGE, Settings.Global.NETSTATS_UID_TAG_DELETE_AGE, Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETPOLICY_QUOTA_ENABLED, Settings.Global.NETPOLICY_QUOTA_UNLIMITED, Settings.Global.NETPOLICY_QUOTA_LIMITED, Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS, Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH, Settings.Global.NETPOLICY_OVERRIDE_ENABLED, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Loading services/core/java/com/android/server/net/NetworkPolicyManagerService.java +49 −10 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,12 @@ import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS; import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH; import static android.provider.Settings.Global.NETPOLICY_QUOTA_LIMITED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_UNLIMITED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; 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_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; Loading Loading @@ -115,6 +121,7 @@ import android.app.PendingIntent; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; Loading Loading @@ -351,6 +358,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ */ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; private static final long QUOTA_UNLIMITED_DEFAULT = DataUnit.MEBIBYTES.toBytes(20); private static final float QUOTA_LIMITED_DEFAULT = 0.1f; private static final float QUOTA_FRAC_JOBS_DEFAULT = 0.5f; private static final float QUOTA_FRAC_MULTIPATH_DEFAULT = 0.5f; private static final int MSG_RULES_CHANGED = 1; private static final int MSG_RULES_CHANGED = 1; private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_LIMIT_REACHED = 5; private static final int MSG_LIMIT_REACHED = 5; Loading Loading @@ -1739,10 +1751,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } mMeteredIfaces = newMeteredIfaces; mMeteredIfaces = newMeteredIfaces; final ContentResolver cr = mContext.getContentResolver(); final boolean quotaEnabled = Settings.Global.getInt(cr, NETPOLICY_QUOTA_ENABLED, 1) != 0; final long quotaUnlimited = Settings.Global.getLong(cr, NETPOLICY_QUOTA_UNLIMITED, QUOTA_UNLIMITED_DEFAULT); final float quotaLimited = Settings.Global.getFloat(cr, NETPOLICY_QUOTA_LIMITED, QUOTA_LIMITED_DEFAULT); // Finally, calculate our opportunistic quotas // Finally, calculate our opportunistic quotas // TODO: add experiments support to disable or tweak ratios mSubscriptionOpportunisticQuota.clear(); mSubscriptionOpportunisticQuota.clear(); for (NetworkState state : states) { for (NetworkState state : states) { if (!quotaEnabled) continue; if (state.network == null) continue; if (state.network == null) continue; final int subId = getSubIdLocked(state.network); final int subId = getSubIdLocked(state.network); final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); Loading @@ -1754,7 +1774,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN; quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN; } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { // Unlimited data; let's use 20MiB/day (600MiB/month) // Unlimited data; let's use 20MiB/day (600MiB/month) quotaBytes = DataUnit.MEBIBYTES.toBytes(20); quotaBytes = quotaUnlimited; } else { } else { // Limited data; let's only use 10% of remaining budget // Limited data; let's only use 10% of remaining budget final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); Loading @@ -1772,7 +1792,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long remainingDays = final long remainingDays = 1 + ((end - now.toEpochMilli() - 1) / TimeUnit.DAYS.toMillis(1)); 1 + ((end - now.toEpochMilli() - 1) / TimeUnit.DAYS.toMillis(1)); quotaBytes = Math.max(0, (remainingBytes / remainingDays) / 10); quotaBytes = Math.max(0, (long) ((remainingBytes / remainingDays) * quotaLimited)); } } mSubscriptionOpportunisticQuota.put(subId, quotaBytes); mSubscriptionOpportunisticQuota.put(subId, quotaBytes); Loading Loading @@ -3048,6 +3068,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } } // Only allow overrides when feature is enabled. However, we always // allow disabling of overrides for safety reasons. final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(), NETPOLICY_OVERRIDE_ENABLED, 1) != 0; if (overrideEnabled || overrideValue == 0) { mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, overrideMask, overrideValue, subId)); overrideMask, overrideValue, subId)); if (timeoutMillis > 0) { if (timeoutMillis > 0) { Loading @@ -3055,6 +3080,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { overrideMask, 0, subId), timeoutMillis); overrideMask, 0, subId), timeoutMillis); } } } } } @Override @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { Loading Loading @@ -4695,11 +4721,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override @Override public long getSubscriptionOpportunisticQuota(Network network, int quotaType) { public long getSubscriptionOpportunisticQuota(Network network, int quotaType) { final long quotaBytes; synchronized (mNetworkPoliciesSecondLock) { synchronized (mNetworkPoliciesSecondLock) { // TODO: handle splitting quota between use-cases quotaBytes = mSubscriptionOpportunisticQuota.get(getSubIdLocked(network), return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network), OPPORTUNISTIC_QUOTA_UNKNOWN); OPPORTUNISTIC_QUOTA_UNKNOWN); } } if (quotaBytes == OPPORTUNISTIC_QUOTA_UNKNOWN) { return OPPORTUNISTIC_QUOTA_UNKNOWN; } if (quotaType == QUOTA_TYPE_JOBS) { return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(), NETPOLICY_QUOTA_FRAC_JOBS, QUOTA_FRAC_JOBS_DEFAULT)); } else if (quotaType == QUOTA_TYPE_MULTIPATH) { return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(), NETPOLICY_QUOTA_FRAC_MULTIPATH, QUOTA_FRAC_MULTIPATH_DEFAULT)); } else { return OPPORTUNISTIC_QUOTA_UNKNOWN; } } } @Override @Override Loading services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +99 −0 Original line number Original line Diff line number Diff line Loading @@ -37,9 +37,12 @@ import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEF import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.text.format.Time.TIMEZONE_UTC; import static android.text.format.Time.TIMEZONE_UTC; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; Loading Loading @@ -1484,6 +1487,102 @@ public class NetworkPolicyManagerServiceTest { true); true); } } @Test public void testOpportunisticQuota() throws Exception { final Network net = new Network(TEST_NET_ID); final NetworkPolicyManagerInternal internal = LocalServices .getService(NetworkPolicyManagerInternal.class); // Create a place to store fake usage final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1)); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) .thenAnswer(new Answer<Long>() { @Override public Long answer(InvocationOnMock invocation) throws Throwable { final NetworkStatsHistory.Entry entry = history.getValues( invocation.getArgument(1), invocation.getArgument(2), null); return entry.rxBytes + entry.txBytes; } }); when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) .thenAnswer(new Answer<NetworkStats>() { @Override public NetworkStats answer(InvocationOnMock invocation) throws Throwable { return stats; } }); // Get active mobile network in place expectMobileDefaults(); mService.updateNetworks(); // We're 20% through the month (6 days) final long start = parseTime("2015-11-01T00:00Z"); final long end = parseTime("2015-11-07T00:00Z"); setCurrentTimeMillis(end); // Get some data usage in place history.clear(); history.recordData(start, end, new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0)); // No data plan { reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // No quotas assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } // Limited data plan { final SubscriptionPlan plan = SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z")) .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED) .build(); mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // We have 1440MB and 24 days left, which is 60MB/day; assuming 10% // for quota split equally between two types gives 3MB. assertEquals(DataUnit.MEGABYTES.toBytes(3), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(DataUnit.MEGABYTES.toBytes(3), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } // Unlimited data plan { final SubscriptionPlan plan = SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z")) .setDataLimit(BYTES_UNLIMITED, LIMIT_BEHAVIOR_DISABLED) .build(); mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // 20MB/day, split equally between two types gives 10MB. assertEquals(DataUnit.MEBIBYTES.toBytes(10), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(DataUnit.MEBIBYTES.toBytes(10), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } } private ApplicationInfo buildApplicationInfo(String label) { private ApplicationInfo buildApplicationInfo(String label) { final ApplicationInfo ai = new ApplicationInfo(); final ApplicationInfo ai = new ApplicationInfo(); ai.nonLocalizedLabel = label; ai.nonLocalizedLabel = label; Loading Loading
core/java/android/provider/Settings.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -8930,6 +8930,20 @@ public final class Settings { /** {@hide} */ /** {@hide} */ public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; /** {@hide} */ public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled"; /** {@hide} */ public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited"; /** {@hide} */ public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited"; /** {@hide} */ public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs"; /** {@hide} */ public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath"; /** {@hide} */ public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled"; /** /** * User preference for which network(s) should be used. Only the * User preference for which network(s) should be used. Only the * connectivity service should touch this. * connectivity service should touch this. Loading
core/tests/coretests/src/android/provider/SettingsBackupTest.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -302,6 +302,12 @@ public class SettingsBackupTest { Settings.Global.NETSTATS_UID_TAG_DELETE_AGE, Settings.Global.NETSTATS_UID_TAG_DELETE_AGE, Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE, Settings.Global.NETPOLICY_QUOTA_ENABLED, Settings.Global.NETPOLICY_QUOTA_UNLIMITED, Settings.Global.NETPOLICY_QUOTA_LIMITED, Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS, Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH, Settings.Global.NETPOLICY_OVERRIDE_ENABLED, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_AVOID_BAD_WIFI, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE, Loading
services/core/java/com/android/server/net/NetworkPolicyManagerService.java +49 −10 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,12 @@ import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS; import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH; import static android.provider.Settings.Global.NETPOLICY_QUOTA_LIMITED; import static android.provider.Settings.Global.NETPOLICY_QUOTA_UNLIMITED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; 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_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; Loading Loading @@ -115,6 +121,7 @@ import android.app.PendingIntent; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; Loading Loading @@ -351,6 +358,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ */ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; private static final long QUOTA_UNLIMITED_DEFAULT = DataUnit.MEBIBYTES.toBytes(20); private static final float QUOTA_LIMITED_DEFAULT = 0.1f; private static final float QUOTA_FRAC_JOBS_DEFAULT = 0.5f; private static final float QUOTA_FRAC_MULTIPATH_DEFAULT = 0.5f; private static final int MSG_RULES_CHANGED = 1; private static final int MSG_RULES_CHANGED = 1; private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_LIMIT_REACHED = 5; private static final int MSG_LIMIT_REACHED = 5; Loading Loading @@ -1739,10 +1751,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } mMeteredIfaces = newMeteredIfaces; mMeteredIfaces = newMeteredIfaces; final ContentResolver cr = mContext.getContentResolver(); final boolean quotaEnabled = Settings.Global.getInt(cr, NETPOLICY_QUOTA_ENABLED, 1) != 0; final long quotaUnlimited = Settings.Global.getLong(cr, NETPOLICY_QUOTA_UNLIMITED, QUOTA_UNLIMITED_DEFAULT); final float quotaLimited = Settings.Global.getFloat(cr, NETPOLICY_QUOTA_LIMITED, QUOTA_LIMITED_DEFAULT); // Finally, calculate our opportunistic quotas // Finally, calculate our opportunistic quotas // TODO: add experiments support to disable or tweak ratios mSubscriptionOpportunisticQuota.clear(); mSubscriptionOpportunisticQuota.clear(); for (NetworkState state : states) { for (NetworkState state : states) { if (!quotaEnabled) continue; if (state.network == null) continue; if (state.network == null) continue; final int subId = getSubIdLocked(state.network); final int subId = getSubIdLocked(state.network); final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); Loading @@ -1754,7 +1774,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN; quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN; } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { // Unlimited data; let's use 20MiB/day (600MiB/month) // Unlimited data; let's use 20MiB/day (600MiB/month) quotaBytes = DataUnit.MEBIBYTES.toBytes(20); quotaBytes = quotaUnlimited; } else { } else { // Limited data; let's only use 10% of remaining budget // Limited data; let's only use 10% of remaining budget final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); Loading @@ -1772,7 +1792,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long remainingDays = final long remainingDays = 1 + ((end - now.toEpochMilli() - 1) / TimeUnit.DAYS.toMillis(1)); 1 + ((end - now.toEpochMilli() - 1) / TimeUnit.DAYS.toMillis(1)); quotaBytes = Math.max(0, (remainingBytes / remainingDays) / 10); quotaBytes = Math.max(0, (long) ((remainingBytes / remainingDays) * quotaLimited)); } } mSubscriptionOpportunisticQuota.put(subId, quotaBytes); mSubscriptionOpportunisticQuota.put(subId, quotaBytes); Loading Loading @@ -3048,6 +3068,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } } // Only allow overrides when feature is enabled. However, we always // allow disabling of overrides for safety reasons. final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(), NETPOLICY_OVERRIDE_ENABLED, 1) != 0; if (overrideEnabled || overrideValue == 0) { mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, overrideMask, overrideValue, subId)); overrideMask, overrideValue, subId)); if (timeoutMillis > 0) { if (timeoutMillis > 0) { Loading @@ -3055,6 +3080,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { overrideMask, 0, subId), timeoutMillis); overrideMask, 0, subId), timeoutMillis); } } } } } @Override @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { Loading Loading @@ -4695,11 +4721,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override @Override public long getSubscriptionOpportunisticQuota(Network network, int quotaType) { public long getSubscriptionOpportunisticQuota(Network network, int quotaType) { final long quotaBytes; synchronized (mNetworkPoliciesSecondLock) { synchronized (mNetworkPoliciesSecondLock) { // TODO: handle splitting quota between use-cases quotaBytes = mSubscriptionOpportunisticQuota.get(getSubIdLocked(network), return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network), OPPORTUNISTIC_QUOTA_UNKNOWN); OPPORTUNISTIC_QUOTA_UNKNOWN); } } if (quotaBytes == OPPORTUNISTIC_QUOTA_UNKNOWN) { return OPPORTUNISTIC_QUOTA_UNKNOWN; } if (quotaType == QUOTA_TYPE_JOBS) { return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(), NETPOLICY_QUOTA_FRAC_JOBS, QUOTA_FRAC_JOBS_DEFAULT)); } else if (quotaType == QUOTA_TYPE_MULTIPATH) { return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(), NETPOLICY_QUOTA_FRAC_MULTIPATH, QUOTA_FRAC_MULTIPATH_DEFAULT)); } else { return OPPORTUNISTIC_QUOTA_UNKNOWN; } } } @Override @Override Loading
services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +99 −0 Original line number Original line Diff line number Diff line Loading @@ -37,9 +37,12 @@ import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEF import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.text.format.Time.TIMEZONE_UTC; import static android.text.format.Time.TIMEZONE_UTC; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; Loading Loading @@ -1484,6 +1487,102 @@ public class NetworkPolicyManagerServiceTest { true); true); } } @Test public void testOpportunisticQuota() throws Exception { final Network net = new Network(TEST_NET_ID); final NetworkPolicyManagerInternal internal = LocalServices .getService(NetworkPolicyManagerInternal.class); // Create a place to store fake usage final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1)); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) .thenAnswer(new Answer<Long>() { @Override public Long answer(InvocationOnMock invocation) throws Throwable { final NetworkStatsHistory.Entry entry = history.getValues( invocation.getArgument(1), invocation.getArgument(2), null); return entry.rxBytes + entry.txBytes; } }); when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) .thenAnswer(new Answer<NetworkStats>() { @Override public NetworkStats answer(InvocationOnMock invocation) throws Throwable { return stats; } }); // Get active mobile network in place expectMobileDefaults(); mService.updateNetworks(); // We're 20% through the month (6 days) final long start = parseTime("2015-11-01T00:00Z"); final long end = parseTime("2015-11-07T00:00Z"); setCurrentTimeMillis(end); // Get some data usage in place history.clear(); history.recordData(start, end, new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0)); // No data plan { reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // No quotas assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } // Limited data plan { final SubscriptionPlan plan = SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z")) .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED) .build(); mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // We have 1440MB and 24 days left, which is 60MB/day; assuming 10% // for quota split equally between two types gives 3MB. assertEquals(DataUnit.MEGABYTES.toBytes(3), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(DataUnit.MEGABYTES.toBytes(3), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } // Unlimited data plan { final SubscriptionPlan plan = SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z")) .setDataLimit(BYTES_UNLIMITED, LIMIT_BEHAVIOR_DISABLED) .build(); mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); expectMobileDefaults(); mService.updateNetworks(); // 20MB/day, split equally between two types gives 10MB. assertEquals(DataUnit.MEBIBYTES.toBytes(10), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS)); assertEquals(DataUnit.MEBIBYTES.toBytes(10), internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH)); } } private ApplicationInfo buildApplicationInfo(String label) { private ApplicationInfo buildApplicationInfo(String label) { final ApplicationInfo ai = new ApplicationInfo(); final ApplicationInfo ai = new ApplicationInfo(); ai.nonLocalizedLabel = label; ai.nonLocalizedLabel = label; Loading