Use batching to set firewall rules.
Netd now provides an option to flush all uids for a given firewall
chain. Using this option is particularly useful when setting the
fw_standby chain, since over time the device will have dozens of idle
apps.
For example, in a Nexus 5X with 45 idle apps, setFirewallUidRules() at
boot was taking 1044ms, and after this change it dropped to just 32ms.
BUG: 26675191
Change-Id: I7016ba646b872f80d0b27a56b867c0b6080d2516
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 22f01ab..0bc1120 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -556,7 +556,7 @@
// chain.
if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall " + name + "UID rules");
for (int i = 0; i < rules.size(); i++) {
- setFirewallUidRuleInternal(chain, rules.keyAt(i), rules.valueAt(i));
+ setFirewallUidRuleLocked(chain, rules.keyAt(i), rules.valueAt(i));
}
}
}
@@ -2240,7 +2240,7 @@
for (int index = uids.length - 1; index >= 0; --index) {
int uid = uids[index];
int rule = rules[index];
- setFirewallUidRule(chain, uid, rule);
+ updateFirewallUidRuleLocked(chain, uid, rule);
newRules.put(uid, rule);
}
// collect the rules to remove.
@@ -2254,7 +2254,25 @@
// remove dead rules
for (int index = rulesToRemove.size() - 1; index >= 0; --index) {
int uid = rulesToRemove.keyAt(index);
- setFirewallUidRuleInternal(chain, uid, FIREWALL_RULE_DEFAULT);
+ updateFirewallUidRuleLocked(chain, uid, FIREWALL_RULE_DEFAULT);
+ }
+ try {
+ switch (chain) {
+ case FIREWALL_CHAIN_DOZABLE:
+ mNetdService.firewallReplaceUidChain("fw_dozable", true, uids);
+ break;
+ case FIREWALL_CHAIN_STANDBY:
+ mNetdService.firewallReplaceUidChain("fw_standby", false, uids);
+ break;
+ case FIREWALL_CHAIN_POWERSAVE:
+ mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
+ break;
+ case FIREWALL_CHAIN_NONE:
+ default:
+ Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error flushing firewall chain " + chain, e);
}
}
}
@@ -2262,44 +2280,48 @@
@Override
public void setFirewallUidRule(int chain, int uid, int rule) {
enforceSystemUid();
- setFirewallUidRuleInternal(chain, uid, rule);
+ synchronized (mQuotaLock) {
+ setFirewallUidRuleLocked(chain, uid, rule);
+ }
}
- private void setFirewallUidRuleInternal(int chain, int uid, int rule) {
- synchronized (mQuotaLock) {
- SparseIntArray uidFirewallRules = getUidFirewallRules(chain);
-
- final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
- if (DBG) {
- Slog.d(TAG, "oldRule = " + oldUidFirewallRule
- + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
- }
- if (oldUidFirewallRule == rule) {
- if (DBG) Slog.d(TAG, "!!!!! Skipping change");
- // TODO: eventually consider throwing
- return;
- }
-
+ private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
+ if (updateFirewallUidRuleLocked(chain, uid, rule)) {
try {
- String ruleName = getFirewallRuleName(chain, rule);
- String oldRuleName = getFirewallRuleName(chain, oldUidFirewallRule);
-
- if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- uidFirewallRules.delete(uid);
- } else {
- uidFirewallRules.put(uid, rule);
- }
-
- if (!ruleName.equals(oldRuleName)) {
- mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
- ruleName);
- }
+ mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
+ getFirewallRuleName(chain, rule));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
}
+ // TODO: now that netd supports batching, NMS should not keep these data structures anymore...
+ private boolean updateFirewallUidRuleLocked(int chain, int uid, int rule) {
+ SparseIntArray uidFirewallRules = getUidFirewallRules(chain);
+
+ final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
+ if (DBG) {
+ Slog.d(TAG, "oldRule = " + oldUidFirewallRule
+ + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
+ }
+ if (oldUidFirewallRule == rule) {
+ if (DBG) Slog.d(TAG, "!!!!! Skipping change");
+ // TODO: eventually consider throwing
+ return false;
+ }
+
+ String ruleName = getFirewallRuleName(chain, rule);
+ String oldRuleName = getFirewallRuleName(chain, oldUidFirewallRule);
+
+ if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
+ uidFirewallRules.delete(uid);
+ } else {
+ uidFirewallRules.put(uid, rule);
+ }
+ return !ruleName.equals(oldRuleName);
+ }
+
private @NonNull String getFirewallRuleName(int chain, int rule) {
String ruleName;
if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {