Wrapping Network and Traffic permissions in a class for module usage.

To add PERMISSION_SDKSANDBOX_LOCALHOST which is used in the same bitmask
as permissions declared in INetd.aidl but doesn't need visibility to the
platform.

Bug: b/326383676
Test: NetworkTrafficPermsTest
Flag: N/A
Change-Id: Id7a36f5659f8fd7542a87671d4b5f067d66db34e
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 4fae73a..6940c7e 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -38,16 +38,16 @@
 import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
 import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.ENODEV;
 import static android.system.OsConstants.ENOENT;
 import static android.system.OsConstants.EOPNOTSUPP;
 
 import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NONE;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_INTERNET;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
 
 import android.app.StatsManager;
 import android.content.Context;
@@ -139,8 +139,8 @@
     private static IBpfMap<U32, Bool> sLocalNetBlockedUidMap = null;
 
     private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
-            Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
-            Pair.create(PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
+            Pair.create(TRAFFIC_PERMISSION_INTERNET, "PERMISSION_INTERNET"),
+            Pair.create(TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
     );
 
     /**
@@ -870,7 +870,8 @@
         }
 
         // Remove the entry if package is uninstalled or uid has only INTERNET permission.
-        if (permissions == PERMISSION_UNINSTALLED || permissions == PERMISSION_INTERNET) {
+        if (permissions == TRAFFIC_PERMISSION_UNINSTALLED
+                || permissions == TRAFFIC_PERMISSION_INTERNET) {
             for (final int uid : uids) {
                 try {
                     sUidPermissionMap.deleteEntry(new S32(uid));
@@ -1013,10 +1014,10 @@
             // Key of uid permission map is appId
             // TODO: Rename map name
             final U8 permissions = sUidPermissionMap.getValue(new S32(appId));
-            return permissions != null ? permissions.val : PERMISSION_INTERNET;
+            return permissions != null ? permissions.val : TRAFFIC_PERMISSION_INTERNET;
         } catch (ErrnoException e) {
             Log.wtf(TAG, "Failed to get permission for uid: " + uid);
-            return PERMISSION_INTERNET;
+            return TRAFFIC_PERMISSION_INTERNET;
         }
     }
 
@@ -1165,7 +1166,7 @@
         if (permissionMask == PERMISSION_NONE) {
             return "PERMISSION_NONE";
         }
-        if (permissionMask == PERMISSION_UNINSTALLED) {
+        if (permissionMask == TRAFFIC_PERMISSION_UNINSTALLED) {
             // PERMISSION_UNINSTALLED should never appear in the map
             return "PERMISSION_UNINSTALLED error!";
         }
diff --git a/service/src/com/android/server/connectivity/NetworkPermissions.java b/service/src/com/android/server/connectivity/NetworkPermissions.java
new file mode 100644
index 0000000..9543d8f
--- /dev/null
+++ b/service/src/com/android/server/connectivity/NetworkPermissions.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.net.INetd;
+
+/**
+ * A wrapper class for managing network and traffic permissions.
+ *
+ * This class encapsulates permissions represented as a bitmask, as defined in INetd.aidl
+ * and used within PermissionMonitor.java.  It distinguishes between two types of permissions:
+ *
+ * 1. Network Permissions: These permissions, declared in INetd.aidl, are used
+ *    by the Android platform's network daemon (system/netd) to control network
+ *    management
+ *
+ * 2. Traffic Permissions: These permissions are used internally by PermissionMonitor.java and
+ *    BpfNetMaps.java to manage fine-grained network traffic filtering and control.
+ *
+ * This wrapper ensures that no new permission definitions, here or in aidl, conflict with any
+ * existing permissions. This prevents unintended interactions or overrides.
+ *
+ * @hide
+ */
+public class NetworkPermissions {
+
+    /*
+     * Below are network permissions declared in INetd.aidl and used by the platform. Using these is
+     * equivalent to using the values in android.net.INetd.
+     */
+    public static final int PERMISSION_NONE = INetd.PERMISSION_NONE; /* 0 */
+    public static final int PERMISSION_NETWORK = INetd.PERMISSION_NETWORK; /* 1 */
+    public static final int PERMISSION_SYSTEM = INetd.PERMISSION_SYSTEM; /* 2 */
+
+    /*
+     * Below are traffic permissions used by PermissionMonitor and BpfNetMaps.
+     */
+
+    /**
+     * PERMISSION_UNINSTALLED is used when an app is uninstalled from the device. All internet
+     * related permissions need to be cleaned.
+     */
+    public static final int TRAFFIC_PERMISSION_UNINSTALLED = -1;
+
+    /**
+     * PERMISSION_INTERNET indicates that the app can create AF_INET and AF_INET6 sockets.
+     */
+    public static final int TRAFFIC_PERMISSION_INTERNET = 4;
+
+    /**
+     * PERMISSION_UPDATE_DEVICE_STATS is used for system UIDs and privileged apps
+     * that have the UPDATE_DEVICE_STATS permission.
+     */
+    public static final int TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS = 8;
+
+    /**
+     * TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST indicates if an SdkSandbox UID will be allowed
+     * to connect to localhost. For non SdkSandbox UIDs this bit is a no-op.
+     */
+    public static final int TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST = 16;
+}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 737e27a..73d077c 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -24,17 +24,17 @@
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NETWORK;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_SYSTEM;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.connectivity.ConnectivityCompatChanges.RESTRICT_LOCAL_NETWORK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NETWORK;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NONE;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_SYSTEM;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_INTERNET;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
 import static com.android.net.module.util.CollectionUtils.toIntArray;
 
 import android.annotation.NonNull;
@@ -72,7 +72,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.modules.utils.build.SdkLevel;
-import com.android.net.flags.Flags;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.SharedLog;
 import com.android.networkstack.apishim.ProcessShimImpl;
@@ -405,7 +404,7 @@
         final SparseIntArray appIdsPerm = new SparseIntArray();
         for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
             final int appId = UserHandle.getAppId(uid);
-            final int permission = appIdsPerm.get(appId) | PERMISSION_INTERNET;
+            final int permission = appIdsPerm.get(appId) | TRAFFIC_PERMISSION_INTERNET;
             appIdsPerm.put(appId, permission);
             if (hasSdkSandbox(appId)) {
                 appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
@@ -413,7 +412,7 @@
         }
         for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
             final int appId = UserHandle.getAppId(uid);
-            final int permission = appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS;
+            final int permission = appIdsPerm.get(appId) | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
             appIdsPerm.put(appId, permission);
             if (hasSdkSandbox(appId)) {
                 appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
@@ -656,7 +655,7 @@
             final int appId = removedUserAppIds.keyAt(i);
             // Need to clear permission if the removed appId is not found in the array.
             if (appIds.indexOfKey(appId) < 0) {
-                appIds.put(appId, PERMISSION_UNINSTALLED);
+                appIds.put(appId, TRAFFIC_PERMISSION_UNINSTALLED);
             }
         }
         sendAppIdsTrafficPermission(appIds);
@@ -708,7 +707,7 @@
             }
         } else {
             // The last package of this uid is removed from device. Clean the package up.
-            permission = PERMISSION_UNINSTALLED;
+            permission = TRAFFIC_PERMISSION_UNINSTALLED;
         }
         return permission;
     }
@@ -751,13 +750,13 @@
                 return "NETWORK";
             case PERMISSION_SYSTEM:
                 return "SYSTEM";
-            case PERMISSION_INTERNET:
+            case TRAFFIC_PERMISSION_INTERNET:
                 return "INTERNET";
-            case PERMISSION_UPDATE_DEVICE_STATS:
+            case TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS:
                 return "UPDATE_DEVICE_STATS";
-            case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+            case (TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS):
                 return "ALL";
-            case PERMISSION_UNINSTALLED:
+            case TRAFFIC_PERMISSION_UNINSTALLED:
                 return "UNINSTALLED";
             default:
                 return "UNKNOWN";
@@ -776,7 +775,7 @@
         // (PERMISSION_UNINSTALLED), remove the appId from the array. Otherwise, update the latest
         // permission to the appId.
         final int appId = UserHandle.getAppId(uid);
-        if (uidTrafficPerm == PERMISSION_UNINSTALLED) {
+        if (uidTrafficPerm == TRAFFIC_PERMISSION_UNINSTALLED) {
             userTrafficPerms.delete(appId);
         } else {
             userTrafficPerms.put(appId, uidTrafficPerm);
@@ -794,7 +793,7 @@
                 installed = true;
             }
         }
-        return installed ? permission : PERMISSION_UNINSTALLED;
+        return installed ? permission : TRAFFIC_PERMISSION_UNINSTALLED;
     }
 
     /**
@@ -931,11 +930,11 @@
         for (int i = 0; i < requestedPermissions.length; i++) {
             if (requestedPermissions[i].equals(INTERNET)
                     && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
-                permissions |= PERMISSION_INTERNET;
+                permissions |= TRAFFIC_PERMISSION_INTERNET;
             }
             if (requestedPermissions[i].equals(UPDATE_DEVICE_STATS)
                     && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
-                permissions |= PERMISSION_UPDATE_DEVICE_STATS;
+                permissions |= TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
             }
         }
         return permissions;
@@ -1174,19 +1173,19 @@
         for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
             int permissions = netdPermissionsAppIds.valueAt(i);
             switch(permissions) {
-                case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+                case (TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS):
                     allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
                     break;
-                case PERMISSION_INTERNET:
+                case TRAFFIC_PERMISSION_INTERNET:
                     internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
                     break;
-                case PERMISSION_UPDATE_DEVICE_STATS:
+                case TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS:
                     updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
                     break;
                 case PERMISSION_NONE:
                     noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
                     break;
-                case PERMISSION_UNINSTALLED:
+                case TRAFFIC_PERMISSION_UNINSTALLED:
                     uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
                     break;
                 default:
@@ -1198,15 +1197,15 @@
             // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
             if (allPermissionAppIds.size() != 0) {
                 mBpfNetMaps.setNetPermForUids(
-                        PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
+                        TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
                         toIntArray(allPermissionAppIds));
             }
             if (internetPermissionAppIds.size() != 0) {
-                mBpfNetMaps.setNetPermForUids(PERMISSION_INTERNET,
+                mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_INTERNET,
                         toIntArray(internetPermissionAppIds));
             }
             if (updateStatsPermissionAppIds.size() != 0) {
-                mBpfNetMaps.setNetPermForUids(PERMISSION_UPDATE_DEVICE_STATS,
+                mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
                         toIntArray(updateStatsPermissionAppIds));
             }
             if (noPermissionAppIds.size() != 0) {
@@ -1214,7 +1213,7 @@
                         toIntArray(noPermissionAppIds));
             }
             if (uninstalledAppIds.size() != 0) {
-                mBpfNetMaps.setNetPermForUids(PERMISSION_UNINSTALLED,
+                mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_UNINSTALLED,
                         toIntArray(uninstalledAppIds));
             }
         } catch (RemoteException | ServiceSpecificException e) {
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt
new file mode 100644
index 0000000..8a9d288
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.INetd
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+class NetworkPermissionsTest {
+    @Test
+    fun test_networkTrafficPerms_correctValues() {
+        assertEquals(NetworkPermissions.PERMISSION_NONE, INetd.PERMISSION_NONE) /* 0 */
+        assertEquals(NetworkPermissions.PERMISSION_NETWORK, INetd.PERMISSION_NETWORK) /* 1 */
+        assertEquals(NetworkPermissions.PERMISSION_SYSTEM, INetd.PERMISSION_SYSTEM) /* 2 */
+        assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_INTERNET, 4)
+        assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS, 8)
+        assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED, -1)
+        assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST, 16)
+    }
+
+    @Test
+    fun test_noOverridesInFlags() {
+        val permsList = listOf(
+            NetworkPermissions.PERMISSION_NONE,
+            NetworkPermissions.PERMISSION_NETWORK,
+            NetworkPermissions.PERMISSION_SYSTEM,
+            NetworkPermissions.TRAFFIC_PERMISSION_INTERNET,
+            NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
+            NetworkPermissions.TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST,
+            NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED
+        )
+        assertFalse(hasDuplicates(permsList))
+    }
+
+    fun hasDuplicates(list: List<Int>): Boolean {
+        return list.distinct().size != list.size
+    }
+}