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
+ }
+}