Add Restricted Mode Firewall Chain

Adding new allowlist firewall chain to support restricted networking
mode. See go/restricted-networking-mode.

Bug: b/157505406
Bug: b/170323408
Test: atest NetworkManagementServiceTest
Change-Id: I8e39b3d7b129ad74224d0c1311135b7b48f6514f
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index e9e242e..4f2292e 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -144,6 +144,8 @@
     public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
     /** @hide */
     public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
+    /** @hide */
+    public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted";
 
     private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c9c318d..181441a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -24,12 +24,14 @@
 import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
 import static android.net.INetd.FIREWALL_CHAIN_NONE;
 import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_DENYLIST;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -218,6 +220,11 @@
      */
     @GuardedBy("mRulesLock")
     private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
+    /**
+     * Contains the per-UID firewall rules that are used when Restricted Networking Mode is enabled.
+     */
+    @GuardedBy("mRulesLock")
+    private SparseIntArray mUidFirewallRestrictedRules = new SparseIntArray();
     /** Set of states for the child firewall chains. True if the chain is active. */
     @GuardedBy("mRulesLock")
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -604,9 +611,15 @@
             syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, "standby ");
             syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, "dozable ");
             syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave ");
+            syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
 
-            final int[] chains =
-                    {FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE};
+            final int[] chains = {
+                    FIREWALL_CHAIN_STANDBY,
+                    FIREWALL_CHAIN_DOZABLE,
+                    FIREWALL_CHAIN_POWERSAVE,
+                    FIREWALL_CHAIN_RESTRICTED
+            };
+
             for (int chain : chains) {
                 if (getFirewallChainState(chain)) {
                     setFirewallChainEnabled(chain, true);
@@ -1708,6 +1721,8 @@
                 return FIREWALL_CHAIN_NAME_DOZABLE;
             case FIREWALL_CHAIN_POWERSAVE:
                 return FIREWALL_CHAIN_NAME_POWERSAVE;
+            case FIREWALL_CHAIN_RESTRICTED:
+                return FIREWALL_CHAIN_NAME_RESTRICTED;
             default:
                 throw new IllegalArgumentException("Bad child chain: " + chain);
         }
@@ -1721,6 +1736,8 @@
                 return FIREWALL_ALLOWLIST;
             case FIREWALL_CHAIN_POWERSAVE:
                 return FIREWALL_ALLOWLIST;
+            case FIREWALL_CHAIN_RESTRICTED:
+                return FIREWALL_ALLOWLIST;
             default:
                 return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
         }
@@ -1765,6 +1782,9 @@
                     case FIREWALL_CHAIN_POWERSAVE:
                         mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
                         break;
+                    case FIREWALL_CHAIN_RESTRICTED:
+                        mNetdService.firewallReplaceUidChain("fw_restricted", true, uids);
+                        break;
                     case FIREWALL_CHAIN_NONE:
                     default:
                         Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
@@ -1849,6 +1869,8 @@
                 return mUidFirewallDozableRules;
             case FIREWALL_CHAIN_POWERSAVE:
                 return mUidFirewallPowerSaveRules;
+            case FIREWALL_CHAIN_RESTRICTED:
+                return mUidFirewallRestrictedRules;
             case FIREWALL_CHAIN_NONE:
                 return mUidFirewallRules;
             default:
@@ -1923,17 +1945,22 @@
         synchronized (mRulesLock) {
             dumpUidFirewallRule(pw, "", mUidFirewallRules);
 
-            pw.print("UID firewall standby chain enabled: "); pw.println(
-                    getFirewallChainState(FIREWALL_CHAIN_STANDBY));
+            pw.print("UID firewall standby chain enabled: ");
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_STANDBY));
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
 
-            pw.print("UID firewall dozable chain enabled: "); pw.println(
-                    getFirewallChainState(FIREWALL_CHAIN_DOZABLE));
+            pw.print("UID firewall dozable chain enabled: ");
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_DOZABLE));
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
 
-            pw.println("UID firewall powersave chain enabled: " +
-                    getFirewallChainState(FIREWALL_CHAIN_POWERSAVE));
+            pw.print("UID firewall powersave chain enabled: ");
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_POWERSAVE));
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules);
+
+            pw.print("UID firewall restricted mode chain enabled: ");
+            pw.println(getFirewallChainState(FIREWALL_CHAIN_RESTRICTED));
+            dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_RESTRICTED,
+                    mUidFirewallRestrictedRules);
         }
 
         synchronized (mIdleTimerLock) {
@@ -2127,6 +2154,11 @@
                 if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of power saver mode");
                 return true;
             }
+            if (getFirewallChainState(FIREWALL_CHAIN_RESTRICTED)
+                    && mUidFirewallRestrictedRules.get(uid) != FIREWALL_RULE_ALLOW) {
+                if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of restricted mode");
+                return true;
+            }
             if (mUidRejectOnMetered.get(uid)) {
                 if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
                         + " in the background");
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 006d78e..5bd352c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -17,11 +17,13 @@
 
 import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
 import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.os.Process.INVALID_UID;
@@ -339,6 +341,8 @@
                 return FIREWALL_CHAIN_NAME_STANDBY;
             case FIREWALL_CHAIN_POWERSAVE:
                 return FIREWALL_CHAIN_NAME_POWERSAVE;
+            case FIREWALL_CHAIN_RESTRICTED:
+                return FIREWALL_CHAIN_NAME_RESTRICTED;
             default:
                 return String.valueOf(chain);
         }
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index b8b5886..ea763d2 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -279,11 +279,18 @@
         isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false);
         isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true);
         expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby);
+        // Restricted mode chain
+        final ArrayMap<Integer, Boolean> isRestrictedForRestrictedMode = new ArrayMap<>();
+        isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+        isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false);
+        isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true);
+        expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode);
 
         final int[] chains = {
                 INetd.FIREWALL_CHAIN_STANDBY,
                 INetd.FIREWALL_CHAIN_POWERSAVE,
-                INetd.FIREWALL_CHAIN_DOZABLE
+                INetd.FIREWALL_CHAIN_DOZABLE,
+                INetd.FIREWALL_CHAIN_RESTRICTED
         };
         final int[] states = {
                 INetd.FIREWALL_RULE_ALLOW,