[SP29] Send interface warning bytes to NetworkStatsProvider
This change contains necessary modification in NPMS and NSS to send warning bytes to NetworkStatsProvider. But since no any provider has been upgraded to handle such parameter. Thus, no behavior change is made in this patch. Test: atest NetworkPolicyManagerServiceTest NetworkStatsServiceTest Test: atest NetworkPolicyManagerServiceTest#testStatsProviderWarningAndLimitReached Bug: 149467454 Bug: 170699770 Bug: 170179169 Ignore-AOSP-First: avoid long automerger delay Change-Id: I6c4863030c36328db571294fd12a40e59864def5
This commit is contained in:
@@ -422,8 +422,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
private static final int MSG_LIMIT_REACHED = 5;
|
private static final int MSG_LIMIT_REACHED = 5;
|
||||||
private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
|
private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
|
||||||
private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
|
private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
|
||||||
private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
|
private static final int MSG_UPDATE_INTERFACE_QUOTAS = 10;
|
||||||
private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
|
private static final int MSG_REMOVE_INTERFACE_QUOTAS = 11;
|
||||||
private static final int MSG_POLICIES_CHANGED = 13;
|
private static final int MSG_POLICIES_CHANGED = 13;
|
||||||
private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
|
private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
|
||||||
private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
|
private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
|
||||||
@@ -2035,33 +2035,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
|
final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
|
||||||
final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
|
final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
|
||||||
long limitBytes = Long.MAX_VALUE;
|
long limitBytes = Long.MAX_VALUE;
|
||||||
if (hasLimit && policy.hasCycle()) {
|
long warningBytes = Long.MAX_VALUE;
|
||||||
|
if ((hasLimit || hasWarning) && policy.hasCycle()) {
|
||||||
final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
|
final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
|
||||||
.cycleIterator(policy).next();
|
.cycleIterator(policy).next();
|
||||||
final long start = cycle.first.toInstant().toEpochMilli();
|
final long start = cycle.first.toInstant().toEpochMilli();
|
||||||
final long end = cycle.second.toInstant().toEpochMilli();
|
final long end = cycle.second.toInstant().toEpochMilli();
|
||||||
final long totalBytes = getTotalBytes(policy.template, start, end);
|
final long totalBytes = getTotalBytes(policy.template, start, end);
|
||||||
|
|
||||||
if (policy.lastLimitSnooze < start) {
|
// If the limit notification is not snoozed, the limit quota needs to be calculated.
|
||||||
|
if (hasLimit && policy.lastLimitSnooze < start) {
|
||||||
// remaining "quota" bytes are based on total usage in
|
// remaining "quota" bytes are based on total usage in
|
||||||
// current cycle. kernel doesn't like 0-byte rules, so we
|
// current cycle. kernel doesn't like 0-byte rules, so we
|
||||||
// set 1-byte quota and disable the radio later.
|
// set 1-byte quota and disable the radio later.
|
||||||
limitBytes = Math.max(1, policy.limitBytes - totalBytes);
|
limitBytes = Math.max(1, policy.limitBytes - totalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the warning notification was snoozed by user, or the service already knows
|
||||||
|
// it is over warning bytes, doesn't need to calculate warning bytes.
|
||||||
|
if (hasWarning && policy.lastWarningSnooze < start
|
||||||
|
&& !policy.isOverWarning(totalBytes)) {
|
||||||
|
warningBytes = Math.max(1, policy.warningBytes - totalBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasLimit || policy.metered) {
|
if (hasWarning || hasLimit || policy.metered) {
|
||||||
if (matchingIfaces.size() > 1) {
|
if (matchingIfaces.size() > 1) {
|
||||||
// TODO: switch to shared quota once NMS supports
|
// TODO: switch to shared quota once NMS supports
|
||||||
Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
|
Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the interface limit. For interfaces which has no cycle, or metered with
|
// Set the interface warning and limit. For interfaces which has no cycle,
|
||||||
// no policy limit, or snoozed limit notification; we still need to put iptables
|
// or metered with no policy quotas, or snoozed notification; we still need to put
|
||||||
// rule hooks to restrict apps for data saver, so push really high quota.
|
// iptables rule hooks to restrict apps for data saver, so push really high quota.
|
||||||
|
// TODO: Push NetworkStatsProvider.QUOTA_UNLIMITED instead of Long.MAX_VALUE to
|
||||||
|
// providers.
|
||||||
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
|
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
|
||||||
final String iface = matchingIfaces.valueAt(j);
|
final String iface = matchingIfaces.valueAt(j);
|
||||||
setInterfaceQuotaAsync(iface, limitBytes);
|
setInterfaceQuotasAsync(iface, warningBytes, limitBytes);
|
||||||
newMeteredIfaces.add(iface);
|
newMeteredIfaces.add(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2084,7 +2095,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
|
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
|
||||||
final String iface = matchingIfaces.valueAt(j);
|
final String iface = matchingIfaces.valueAt(j);
|
||||||
if (!newMeteredIfaces.contains(iface)) {
|
if (!newMeteredIfaces.contains(iface)) {
|
||||||
setInterfaceQuotaAsync(iface, Long.MAX_VALUE);
|
setInterfaceQuotasAsync(iface, Long.MAX_VALUE, Long.MAX_VALUE);
|
||||||
newMeteredIfaces.add(iface);
|
newMeteredIfaces.add(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2096,7 +2107,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
|
for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
|
||||||
final String iface = mMeteredIfaces.valueAt(i);
|
final String iface = mMeteredIfaces.valueAt(i);
|
||||||
if (!newMeteredIfaces.contains(iface)) {
|
if (!newMeteredIfaces.contains(iface)) {
|
||||||
removeInterfaceQuotaAsync(iface);
|
removeInterfaceQuotasAsync(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mMeteredIfaces = newMeteredIfaces;
|
mMeteredIfaces = newMeteredIfaces;
|
||||||
@@ -5036,19 +5047,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
mNetworkStats.advisePersistThreshold(persistThreshold);
|
mNetworkStats.advisePersistThreshold(persistThreshold);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MSG_UPDATE_INTERFACE_QUOTA: {
|
case MSG_UPDATE_INTERFACE_QUOTAS: {
|
||||||
final String iface = (String) msg.obj;
|
final IfaceQuotas val = (IfaceQuotas) msg.obj;
|
||||||
// int params need to be stitched back into a long
|
// TODO: Consider set a new limit before removing the original one.
|
||||||
final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
|
removeInterfaceLimit(val.iface);
|
||||||
removeInterfaceQuota(iface);
|
setInterfaceLimit(val.iface, val.limit);
|
||||||
setInterfaceQuota(iface, quota);
|
mNetworkStats.setStatsProviderWarningAndLimitAsync(val.iface, val.warning,
|
||||||
mNetworkStats.setStatsProviderLimitAsync(iface, quota);
|
val.limit);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MSG_REMOVE_INTERFACE_QUOTA: {
|
case MSG_REMOVE_INTERFACE_QUOTAS: {
|
||||||
final String iface = (String) msg.obj;
|
final String iface = (String) msg.obj;
|
||||||
removeInterfaceQuota(iface);
|
removeInterfaceLimit(iface);
|
||||||
mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED);
|
mNetworkStats.setStatsProviderWarningAndLimitAsync(iface, QUOTA_UNLIMITED,
|
||||||
|
QUOTA_UNLIMITED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MSG_RESET_FIREWALL_RULES_BY_UID: {
|
case MSG_RESET_FIREWALL_RULES_BY_UID: {
|
||||||
@@ -5196,15 +5208,32 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInterfaceQuotaAsync(String iface, long quotaBytes) {
|
private static final class IfaceQuotas {
|
||||||
// long quotaBytes split up into two ints to fit in message
|
@NonNull public final String iface;
|
||||||
mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32),
|
// Warning and limit bytes of interface qutoas, could be QUOTA_UNLIMITED or Long.MAX_VALUE
|
||||||
(int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget();
|
// if not set. 0 is not acceptable since kernel doesn't like 0-byte rules.
|
||||||
|
public final long warning;
|
||||||
|
public final long limit;
|
||||||
|
|
||||||
|
private IfaceQuotas(@NonNull String iface, long warning, long limit) {
|
||||||
|
this.iface = iface;
|
||||||
|
this.warning = warning;
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInterfaceQuota(String iface, long quotaBytes) {
|
private void setInterfaceQuotasAsync(@NonNull String iface,
|
||||||
|
long warningBytes, long limitBytes) {
|
||||||
|
mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTAS,
|
||||||
|
new IfaceQuotas(iface, warningBytes, limitBytes)).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInterfaceLimit(String iface, long limitBytes) {
|
||||||
try {
|
try {
|
||||||
mNetworkManager.setInterfaceQuota(iface, quotaBytes);
|
// For legacy design the data warning is covered by global alert, where the
|
||||||
|
// kernel will notify upper layer for a small amount of change of traffic
|
||||||
|
// statistics. Thus, passing warning is not needed.
|
||||||
|
mNetworkManager.setInterfaceQuota(iface, limitBytes);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
Log.wtf(TAG, "problem setting interface quota", e);
|
Log.wtf(TAG, "problem setting interface quota", e);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -5212,11 +5241,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeInterfaceQuotaAsync(String iface) {
|
private void removeInterfaceQuotasAsync(String iface) {
|
||||||
mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget();
|
mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTAS, iface).sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeInterfaceQuota(String iface) {
|
private void removeInterfaceLimit(String iface) {
|
||||||
try {
|
try {
|
||||||
mNetworkManager.removeInterfaceQuota(iface);
|
mNetworkManager.removeInterfaceQuota(iface);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
|
|||||||
@@ -37,8 +37,9 @@ public abstract class NetworkStatsManagerInternal {
|
|||||||
public abstract void forceUpdate();
|
public abstract void forceUpdate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the quota limit to all registered custom network stats providers.
|
* Set the warning and limit to all registered custom network stats providers.
|
||||||
* Note that invocation of any interface will be sent to all providers.
|
* Note that invocation of any interface will be sent to all providers.
|
||||||
*/
|
*/
|
||||||
public abstract void setStatsProviderLimitAsync(@NonNull String iface, long quota);
|
public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
|
||||||
|
long limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1674,11 +1674,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
|
public void setStatsProviderWarningAndLimitAsync(
|
||||||
if (LOGV) Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
|
@NonNull String iface, long warning, long limit) {
|
||||||
// TODO: Set warning accordingly.
|
if (LOGV) {
|
||||||
|
Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
|
||||||
|
+ iface + "," + warning + "," + limit + ")");
|
||||||
|
}
|
||||||
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
|
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
|
||||||
NetworkStatsProvider.QUOTA_UNLIMITED, quota));
|
warning, limit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1773,57 +1773,75 @@ public class NetworkPolicyManagerServiceTest {
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
|
||||||
* Test that when StatsProvider triggers limit reached, new limit will be calculated and
|
|
||||||
* re-armed.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testStatsProviderLimitReached() throws Exception {
|
|
||||||
final int CYCLE_DAY = 15;
|
|
||||||
|
|
||||||
final NetworkStats stats = new NetworkStats(0L, 1);
|
|
||||||
stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
|
stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
|
||||||
2999, 1, 2000, 1, 0);
|
rxBytes, 1, txBytes, 1, 0);
|
||||||
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
|
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
|
||||||
.thenReturn(stats.getTotalBytes());
|
.thenReturn(stats.getTotalBytes());
|
||||||
when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
|
when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
|
||||||
.thenReturn(stats);
|
.thenReturn(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
|
||||||
|
final NetworkPolicyManagerInternal npmi = LocalServices
|
||||||
|
.getService(NetworkPolicyManagerInternal.class);
|
||||||
|
npmi.onStatsProviderWarningOrLimitReached("TEST");
|
||||||
|
// Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
|
||||||
|
postMsgAndWaitForCompletion();
|
||||||
|
verify(mStatsService).forceUpdate();
|
||||||
|
// Wait for processing of MSG_*_INTERFACE_QUOTAS.
|
||||||
|
postMsgAndWaitForCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that when StatsProvider triggers warning and limit reached, new quotas will be
|
||||||
|
* calculated and re-armed.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStatsProviderWarningAndLimitReached() throws Exception {
|
||||||
|
final int CYCLE_DAY = 15;
|
||||||
|
|
||||||
|
final NetworkStats stats = new NetworkStats(0L, 1);
|
||||||
|
increaseMockedTotalBytes(stats, 2999, 2000);
|
||||||
|
|
||||||
// Get active mobile network in place
|
// Get active mobile network in place
|
||||||
expectMobileDefaults();
|
expectMobileDefaults();
|
||||||
mService.updateNetworks();
|
mService.updateNetworks();
|
||||||
verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, Long.MAX_VALUE);
|
verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
|
||||||
|
Long.MAX_VALUE);
|
||||||
|
|
||||||
// Set limit to 10KB.
|
// Set warning to 7KB and limit to 10KB.
|
||||||
setNetworkPolicies(new NetworkPolicy(
|
setNetworkPolicies(new NetworkPolicy(
|
||||||
sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L,
|
sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, 7000L, 10000L, true));
|
||||||
true));
|
|
||||||
postMsgAndWaitForCompletion();
|
postMsgAndWaitForCompletion();
|
||||||
|
|
||||||
// Verifies that remaining quota is set to providers.
|
// Verifies that remaining quotas are set to providers.
|
||||||
verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L);
|
verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
|
||||||
|
|
||||||
reset(mStatsService);
|
reset(mStatsService);
|
||||||
|
|
||||||
// Increase the usage.
|
// Increase the usage and simulates that limit reached fires earlier by provider,
|
||||||
stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
|
// but actually the quota is not yet reached. Verifies that the limit reached leads to
|
||||||
1000, 1, 999, 1, 0);
|
// a force update and new quotas should be set.
|
||||||
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
|
increaseMockedTotalBytes(stats, 1000, 999);
|
||||||
.thenReturn(stats.getTotalBytes());
|
triggerOnStatsProviderWarningOrLimitReached();
|
||||||
when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
|
verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
|
||||||
.thenReturn(stats);
|
reset(mStatsService);
|
||||||
|
|
||||||
// Simulates that limit reached fires earlier by provider, but actually the quota is not
|
// Increase the usage and simulate warning reached, the new warning should be unlimited
|
||||||
// yet reached.
|
// since service will disable warning quota to stop lower layer from keep triggering
|
||||||
final NetworkPolicyManagerInternal npmi = LocalServices
|
// warning reached event.
|
||||||
.getService(NetworkPolicyManagerInternal.class);
|
increaseMockedTotalBytes(stats, 1000L, 1000);
|
||||||
npmi.onStatsProviderWarningOrLimitReached("TEST");
|
triggerOnStatsProviderWarningOrLimitReached();
|
||||||
|
verify(mStatsService).setStatsProviderWarningAndLimitAsync(
|
||||||
|
TEST_IFACE, Long.MAX_VALUE, 1002L);
|
||||||
|
reset(mStatsService);
|
||||||
|
|
||||||
// Verifies that the limit reached leads to a force update and new limit should be set.
|
// Increase the usage that over the warning and limit, the new limit should set to 1 to
|
||||||
postMsgAndWaitForCompletion();
|
// block the network traffic.
|
||||||
verify(mStatsService).forceUpdate();
|
increaseMockedTotalBytes(stats, 1000L, 1000);
|
||||||
postMsgAndWaitForCompletion();
|
triggerOnStatsProviderWarningOrLimitReached();
|
||||||
verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L - 1999L);
|
verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
|
||||||
|
reset(mStatsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user