Merge "Creating interface name to index tracking for Bpf Maps." into main
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index 5f66f47..5ff708d 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -21,7 +21,6 @@
import static android.net.NetworkStats.UID_ALL;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.net.NetworkStats;
import android.net.UnderlyingNetworkInfo;
@@ -31,6 +30,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.BpfNetMaps;
+import com.android.server.connectivity.InterfaceTracker;
import java.io.IOException;
import java.net.ProtocolException;
@@ -108,7 +108,7 @@
/** Create a new {@link BpfNetMaps}. */
public BpfNetMaps createBpfNetMaps(@NonNull Context ctx) {
- return new BpfNetMaps(ctx);
+ return new BpfNetMaps(ctx, new InterfaceTracker(ctx));
}
}
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 36c0cf9..25c0617 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -49,6 +49,8 @@
import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED;
import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.StatsManager;
import android.content.Context;
import android.net.BpfNetMapsUtils;
@@ -85,12 +87,14 @@
import com.android.net.module.util.bpf.IngressDiscardKey;
import com.android.net.module.util.bpf.IngressDiscardValue;
import com.android.net.module.util.bpf.LocalNetAccessKey;
+import com.android.server.connectivity.InterfaceTracker;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -142,6 +146,7 @@
Pair.create(TRAFFIC_PERMISSION_INTERNET, "PERMISSION_INTERNET"),
Pair.create(TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
);
+ private final InterfaceTracker mInterfaceTracker;
/**
* Set configurationMap for test.
@@ -423,23 +428,27 @@
/** Constructor used after T that doesn't need to use netd anymore. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public BpfNetMaps(final Context context) {
- this(context, null);
+ public BpfNetMaps(final Context context, @NonNull final InterfaceTracker interfaceTracker) {
+ this(context, null, interfaceTracker);
if (!SdkLevel.isAtLeastT()) throw new IllegalArgumentException("BpfNetMaps need to use netd before T");
}
- public BpfNetMaps(final Context context, final INetd netd) {
- this(context, netd, new Dependencies());
+ public BpfNetMaps(final Context context, final INetd netd, @NonNull final InterfaceTracker
+ interfaceTracker) {
+ this(context, netd, new Dependencies(), interfaceTracker);
}
@VisibleForTesting
- public BpfNetMaps(final Context context, final INetd netd, final Dependencies deps) {
+ public BpfNetMaps(final Context context, final INetd netd, final Dependencies deps,
+ @NonNull final InterfaceTracker interfaceTracker) {
+ Objects.requireNonNull(interfaceTracker);
if (SdkLevel.isAtLeastT()) {
ensureInitialized(context);
}
mNetd = netd;
mDeps = deps;
+ mInterfaceTracker = interfaceTracker;
}
private void maybeThrow(final int err, final String msg) {
@@ -902,7 +911,7 @@
* @param isAllowed is the local network call allowed or blocked.
*/
@RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
- public void addLocalNetAccess(final int lpmBitlen, final String iface,
+ public void addLocalNetAccess(final int lpmBitlen, @Nullable final String iface,
final InetAddress address, final int protocol, final int remotePort,
final boolean isAllowed) {
throwIfPre25Q2("addLocalNetAccess is not available on pre-B devices");
@@ -910,7 +919,7 @@
if (iface == null) {
ifIndex = 0;
} else {
- ifIndex = mDeps.getIfIndex(iface);
+ ifIndex = mInterfaceTracker.getInterfaceIndex(iface);
}
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, skip addLocalNetAccess for " + address
@@ -937,14 +946,14 @@
* @param remotePort src/dst port for ingress/egress
*/
@RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
- public void removeLocalNetAccess(final int lpmBitlen, final String iface,
+ public void removeLocalNetAccess(final int lpmBitlen, @Nullable final String iface,
final InetAddress address, final int protocol, final int remotePort) {
throwIfPre25Q2("removeLocalNetAccess is not available on pre-B devices");
final int ifIndex;
if (iface == null) {
ifIndex = 0;
} else {
- ifIndex = mDeps.getIfIndex(iface);
+ ifIndex = mInterfaceTracker.getInterfaceIndex(iface);
}
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, skip removeLocalNetAccess for " + address
@@ -973,14 +982,14 @@
* is not local network or if configuration is allowed like local dns servers.
*/
@RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
- public boolean getLocalNetAccess(final int lpmBitlen, final String iface,
+ public boolean getLocalNetAccess(final int lpmBitlen, @Nullable final String iface,
final InetAddress address, final int protocol, final int remotePort) {
throwIfPre25Q2("getLocalNetAccess is not available on pre-B devices");
final int ifIndex;
if (iface == null) {
ifIndex = 0;
} else {
- ifIndex = mDeps.getIfIndex(iface);
+ ifIndex = mInterfaceTracker.getInterfaceIndex(iface);
}
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, returning default from getLocalNetAccess for "
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index b2e49e7..3ce3f02 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -365,6 +365,7 @@
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.DscpPolicyTracker;
import com.android.server.connectivity.FullScore;
+import com.android.server.connectivity.InterfaceTracker;
import com.android.server.connectivity.InvalidTagException;
import com.android.server.connectivity.KeepaliveResourceUtil;
import com.android.server.connectivity.KeepaliveTracker;
@@ -577,6 +578,7 @@
private final NetworkStatsManager mStatsManager;
private final NetworkPolicyManager mPolicyManager;
private final BpfNetMaps mBpfNetMaps;
+ private final InterfaceTracker mInterfaceTracker;
/**
* TestNetworkService (lazily) created upon first usage. Locked to prevent creation of multiple
@@ -1662,8 +1664,17 @@
* @param netd a netd binder
* @return BpfNetMaps implementation.
*/
- public BpfNetMaps getBpfNetMaps(Context context, INetd netd) {
- return new BpfNetMaps(context, netd);
+ public BpfNetMaps getBpfNetMaps(Context context, INetd netd,
+ InterfaceTracker interfaceTracker) {
+ return new BpfNetMaps(context, netd, interfaceTracker);
+ }
+
+ /**
+ * Get the InterfaceTracker implementation to use in ConnectivityService.
+ * @return InterfaceTracker implementation.
+ */
+ public InterfaceTracker getInterfaceTracker(Context context) {
+ return new InterfaceTracker(context);
}
/**
@@ -1886,7 +1897,8 @@
mWakeUpMask = mask;
mNetd = netd;
- mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
+ mInterfaceTracker = mDeps.getInterfaceTracker(mContext);
+ mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd, mInterfaceTracker);
mHandlerThread = mDeps.makeHandlerThread("ConnectivityServiceThread");
mPermissionMonitorDeps = mPermDeps;
mPermissionMonitor =
@@ -9605,8 +9617,6 @@
updateIngressToVpnAddressFiltering(newLp, oldLp, networkAgent);
- updateLocalNetworkAddresses(newLp, oldLp);
-
updateMtu(newLp, oldLp);
// TODO - figure out what to do for clat
// for (LinkProperties lp : newLp.getStackedLinks()) {
@@ -9769,16 +9779,23 @@
wakeupModifyInterface(iface, nai, true);
mDeps.reportNetworkInterfaceForTransports(mContext, iface,
nai.networkCapabilities.getTransportTypes());
+ mInterfaceTracker.addInterface(iface);
} catch (Exception e) {
logw("Exception adding interface: " + e);
}
}
}
+
+ // The local network addresses needs to be updated before interfaces are removed because
+ // modifying bpf map local_net_access requires mapping interface name to index.
+ updateLocalNetworkAddresses(newLp, oldLp);
+
for (final String iface : interfaceDiff.removed) {
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, nai, false);
mRoutingCoordinatorService.removeInterfaceFromNetwork(netId, iface);
+ mInterfaceTracker.removeInterface(iface);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
diff --git a/service/src/com/android/server/connectivity/InterfaceTracker.java b/service/src/com/android/server/connectivity/InterfaceTracker.java
new file mode 100644
index 0000000..0b4abeb
--- /dev/null
+++ b/service/src/com/android/server/connectivity/InterfaceTracker.java
@@ -0,0 +1,123 @@
+/*
+ * 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.annotation.Nullable;
+import android.content.Context;
+import android.system.Os;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BpfNetMaps;
+
+import java.util.Map;
+
+/**
+ * InterfaceTracker is responsible for providing interface mapping and tracking.
+ * @hide
+ */
+public class InterfaceTracker {
+ static {
+ if (BpfNetMaps.isAtLeast25Q2()) {
+ System.loadLibrary("service-connectivity");
+ }
+ }
+ private static final String TAG = "InterfaceTracker";
+ private final Dependencies mDeps;
+ private final Map<String, Integer> mInterfaceMap;
+
+ public InterfaceTracker(final Context context) {
+ this(context, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public InterfaceTracker(final Context context, final Dependencies deps) {
+ this.mInterfaceMap = new ArrayMap<>();
+ this.mDeps = deps;
+ }
+
+ /**
+ * To add interface to tracking
+ * @param interfaceName name of interface added.
+ */
+ public void addInterface(@Nullable final String interfaceName) {
+ final int interfaceIndex;
+ if (interfaceName == null) {
+ interfaceIndex = 0;
+ } else {
+ interfaceIndex = mDeps.getIfIndex(interfaceName);
+ }
+ if (interfaceIndex == 0) {
+ Log.e(TAG, "Failed to get interface index for " + interfaceName);
+ return;
+ }
+ synchronized (mInterfaceMap) {
+ mInterfaceMap.put(interfaceName, interfaceIndex);
+ }
+ }
+
+ /**
+ * To remove interface from tracking
+ * @param interfaceName name of interface removed.
+ * @return true if the value was present and now removed.
+ */
+ public boolean removeInterface(@Nullable final String interfaceName) {
+ if (interfaceName == null) return false;
+ synchronized (mInterfaceMap) {
+ return mInterfaceMap.remove(interfaceName) != null;
+ }
+ }
+
+ /**
+ * Get interface index from interface name.
+ * @param interfaceName name of interface
+ * @return interface index for given interface name or 0 if interface is not found.
+ */
+ public int getInterfaceIndex(@Nullable final String interfaceName) {
+ final int interfaceIndex;
+ if (interfaceName != null) {
+ synchronized (mInterfaceMap) {
+ interfaceIndex = mInterfaceMap.getOrDefault(interfaceName, 0);
+ }
+ } else {
+ interfaceIndex = 0;
+ }
+ return interfaceIndex;
+ }
+
+ /**
+ * Dependencies of InterfaceTracker, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get interface index.
+ */
+ public int getIfIndex(final String ifName) {
+ return Os.if_nametoindex(ifName);
+ }
+
+ /**
+ * Get interface name
+ */
+ public String getIfName(final int ifIndex) {
+ return Os.if_indextoname(ifIndex);
+ }
+
+ }
+}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 437eb81..de39215 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -55,11 +55,11 @@
import com.android.networkstack.apishim.TelephonyManagerShimImpl
import com.android.server.BpfNetMaps
import com.android.server.ConnectivityService
-import com.android.server.L2capNetworkProvider
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
import com.android.server.connectivity.CarrierPrivilegeAuthenticator
import com.android.server.connectivity.ConnectivityResources
+import com.android.server.connectivity.InterfaceTracker
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.PermissionMonitor
@@ -114,6 +114,8 @@
@Mock
private lateinit var netd: INetd
@Mock
+ private lateinit var interfaceTracker: InterfaceTracker
+ @Mock
private lateinit var dnsResolver: IDnsResolver
@Mock
private lateinit var systemConfigManager: SystemConfigManager
@@ -140,11 +142,15 @@
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
private val httpProbeUrl get() =
- realContext.getResources().getString(com.android.server.net.integrationtests.R.string
- .config_captive_portal_http_url)
+ realContext.getResources().getString(
+ com.android.server.net.integrationtests.R.string
+ .config_captive_portal_http_url
+ )
private val httpsProbeUrl get() =
- realContext.getResources().getString(com.android.server.net.integrationtests.R.string
- .config_captive_portal_https_url)
+ realContext.getResources().getString(
+ com.android.server.net.integrationtests.R.string
+ .config_captive_portal_https_url
+ )
private class InstrumentationServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -166,12 +172,19 @@
fun setUpClass() {
val intent = Intent(realContext, NetworkStackInstrumentationService::class.java)
intent.action = INetworkStackInstrumentation::class.qualifiedName
- assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(),
- BIND_AUTO_CREATE or BIND_IMPORTANT),
- "Error binding to instrumentation service")
- assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS),
+ assertTrue(
+ realContext.bindService(
+ intent,
+ InstrumentationServiceConnection(),
+ BIND_AUTO_CREATE or BIND_IMPORTANT
+ ),
+ "Error binding to instrumentation service"
+ )
+ assertTrue(
+ bindingCondition.block(SERVICE_BIND_TIMEOUT_MS),
"Timed out binding to instrumentation service " +
- "after $SERVICE_BIND_TIMEOUT_MS ms")
+ "after $SERVICE_BIND_TIMEOUT_MS ms"
+ )
}
}
@@ -201,7 +214,8 @@
// We don't test the actual notification value strings, so just return an empty array.
// It doesn't matter what the values are as long as it's not null.
doReturn(emptyArray<String>()).`when`(resources).getStringArray(
- R.array.network_switch_type_name)
+ R.array.network_switch_type_name
+ )
doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
doReturn(R.array.config_networkSupportedKeepaliveCount).`when`(resources)
.getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any())
@@ -223,7 +237,13 @@
}
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
- context, dnsResolver, log, netd, deps, PermissionMonitorDependencies())
+ context,
+ dnsResolver,
+ log,
+ netd,
+ deps,
+ PermissionMonitorDependencies()
+ )
private inner class TestDependencies : ConnectivityService.Dependencies() {
override fun getNetworkStack() = networkStackClient
@@ -231,7 +251,11 @@
mock(ProxyTracker::class.java)
override fun getSystemProperties() = mock(MockableSystemProperties::class.java)
override fun makeNetIdManager() = TestNetIdManager()
- override fun getBpfNetMaps(context: Context?, netd: INetd?) = mock(BpfNetMaps::class.java)
+ override fun getBpfNetMaps(
+ context: Context?,
+ netd: INetd?,
+ interfaceTracker: InterfaceTracker?
+ ) = mock(BpfNetMaps::class.java)
.also {
doReturn(PERMISSION_INTERNET).`when`(it).getNetPermForUid(anyInt())
}
@@ -241,13 +265,17 @@
c: Context,
h: Handler,
r: Runnable
- ) = MultinetworkPolicyTracker(c, h, r,
+ ) = MultinetworkPolicyTracker(
+ c,
+ h,
+ r,
object : MultinetworkPolicyTracker.Dependencies() {
override fun getResourcesForActiveSubId(
connResources: ConnectivityResources,
activeSubId: Int
) = resources
- })
+ }
+ )
override fun makeHandlerThread(tag: String): HandlerThread =
super.makeHandlerThread(tag).also { handlerThreads.add(it) }
@@ -259,13 +287,18 @@
listener: BiConsumer<Int, Int>,
handler: Handler
): CarrierPrivilegeAuthenticator {
- return CarrierPrivilegeAuthenticator(context,
+ return CarrierPrivilegeAuthenticator(
+ context,
object : CarrierPrivilegeAuthenticator.Dependencies() {
override fun makeHandlerThread(): HandlerThread =
super.makeHandlerThread().also { handlerThreads.add(it) }
},
- tm, TelephonyManagerShimImpl.newInstance(tm),
- requestRestrictedWifiEnabled, listener, handler)
+ tm,
+ TelephonyManagerShimImpl.newInstance(tm),
+ requestRestrictedWifiEnabled,
+ listener,
+ handler
+ )
}
override fun makeSatelliteAccessController(
@@ -273,7 +306,8 @@
updateSatellitePreferredUid: Consumer<MutableSet<Int>>?,
connectivityServiceInternalHandler: Handler
): SatelliteAccessController? = mock(
- SatelliteAccessController::class.java)
+ SatelliteAccessController::class.java
+ )
override fun makeL2capNetworkProvider(context: Context) = null
}
@@ -305,8 +339,12 @@
nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */,
- context)
+ val na = NetworkAgentWrapper(
+ TRANSPORT_CELLULAR,
+ LinkProperties(),
+ null /* ncTemplate */,
+ context
+ )
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
@@ -344,7 +382,8 @@
| "user-portal-url": "https://login.capport.android.com",
| "venue-info-url": "https://venueinfo.capport.android.com"
|}
- """.trimMargin()))
+ """.trimMargin()
+ ))
// Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the
// HTTP probe as it should not be sent.
@@ -398,8 +437,10 @@
BpfUtils.BPF_CGROUP_INET_EGRESS,
BpfUtils.BPF_CGROUP_INET_SOCK_CREATE
).forEach {
- val ret = SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
- "cmd connectivity bpf-get-cgroup-program-id $it").trim()
+ val ret = SystemUtil.runShellCommand(
+ InstrumentationRegistry.getInstrumentation(),
+ "cmd connectivity bpf-get-cgroup-program-id $it"
+ ).trim()
assertTrue(Integer.parseInt(ret) > 0, "Unexpected output $ret for type $it")
}
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index caf1765..1d2e8b0 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -109,6 +109,7 @@
import com.android.net.module.util.bpf.IngressDiscardKey;
import com.android.net.module.util.bpf.IngressDiscardValue;
import com.android.net.module.util.bpf.LocalNetAccessKey;
+import com.android.server.connectivity.InterfaceTracker;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -170,6 +171,8 @@
@Mock INetd mNetd;
@Mock BpfNetMaps.Dependencies mDeps;
@Mock Context mContext;
+
+ @Mock InterfaceTracker mInterfaceTracker;
private final IBpfMap<S32, U32> mConfigurationMap = new TestBpfMap<>(S32.class, U32.class);
private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap =
new TestBpfMap<>(S32.class, UidOwnerValue.class);
@@ -188,6 +191,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(TEST_IF_INDEX).when(mDeps).getIfIndex(TEST_IF_NAME);
+ doReturn(TEST_IF_INDEX).when(mInterfaceTracker).getInterfaceIndex(TEST_IF_NAME);
doReturn(TEST_IF_NAME).when(mDeps).getIfName(TEST_IF_INDEX);
doReturn(0).when(mDeps).synchronizeKernelRCU();
BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
@@ -202,7 +206,7 @@
BpfNetMaps.setDataSaverEnabledMapForTest(mDataSaverEnabledMap);
mDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(DATA_SAVER_DISABLED));
BpfNetMaps.setIngressDiscardMapForTest(mIngressDiscardMap);
- mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps);
+ mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps, mInterfaceTracker);
}
@Test
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c4944b6..c28a0f8 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -413,6 +413,7 @@
import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.connectivity.InterfaceTracker;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.MultinetworkPolicyTracker;
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies;
@@ -2262,7 +2263,8 @@
}
@Override
- public BpfNetMaps getBpfNetMaps(Context context, INetd netd) {
+ public BpfNetMaps getBpfNetMaps(Context context, INetd netd,
+ InterfaceTracker interfaceTracker) {
return mBpfNetMaps;
}
diff --git a/tests/unit/java/com/android/server/connectivity/InterfaceTrackerTest.java b/tests/unit/java/com/android/server/connectivity/InterfaceTrackerTest.java
new file mode 100644
index 0000000..8a9ada0
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/InterfaceTrackerTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.ConnectivityModuleTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@ConnectivityModuleTest
+public class InterfaceTrackerTest {
+ private static final String TAG = "InterfaceTrackerTest";
+ private static final String TEST_IF_NAME = "wlan10";
+ private static final String TEST_INCORRECT_IF_NAME = "wlan20";
+ private static final int TEST_IF_INDEX = 7;
+
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+ private InterfaceTracker mInterfaceTracker;
+
+ @Mock Context mContext;
+ @Mock InterfaceTracker.Dependencies mDeps;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(TEST_IF_INDEX).when(mDeps).getIfIndex(TEST_IF_NAME);
+ mInterfaceTracker = new InterfaceTracker(mContext, mDeps);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testAddingInterface_InterfaceNameIndexMappingAdded() {
+ mInterfaceTracker.addInterface(TEST_IF_NAME);
+ assertEquals(TEST_IF_INDEX, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testAddingNullInterface_InterfaceNameIndexMappingNotAdded() {
+ mInterfaceTracker.addInterface(null);
+ assertEquals(0, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testAddingIncorrectInterface_InterfaceNameIndexMappingNotAdded() {
+ mInterfaceTracker.addInterface(TEST_INCORRECT_IF_NAME);
+
+ assertEquals(0, mInterfaceTracker.getInterfaceIndex(TEST_INCORRECT_IF_NAME));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testRemovingInterface_InterfaceNameIndexMappingRemoved() {
+ mInterfaceTracker.addInterface(TEST_IF_NAME);
+ assertEquals(TEST_IF_INDEX, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ mInterfaceTracker.removeInterface(TEST_IF_NAME);
+ assertEquals(0, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testRemovingNullInterface_InterfaceNameIndexMappingNotRemoved() {
+ mInterfaceTracker.addInterface(TEST_IF_NAME);
+ mInterfaceTracker.removeInterface(null);
+ assertEquals(TEST_IF_INDEX, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testRemovingIncorrectInterface_InterfaceNameIndexMappingNotRemoved() {
+ mInterfaceTracker.addInterface(TEST_IF_NAME);
+ mInterfaceTracker.removeInterface(TEST_INCORRECT_IF_NAME);
+ assertEquals(TEST_IF_INDEX, mInterfaceTracker.getInterfaceIndex(TEST_IF_NAME));
+ }
+
+}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 557bfd6..d7e781e 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -68,6 +68,7 @@
import com.android.server.connectivity.CarrierPrivilegeAuthenticator
import com.android.server.connectivity.ClatCoordinator
import com.android.server.connectivity.ConnectivityFlags
+import com.android.server.connectivity.InterfaceTracker
import com.android.server.connectivity.MulticastRoutingCoordinatorService
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies
@@ -193,6 +194,7 @@
val connResources = makeMockConnResources(sysResources, packageManager)
val netd = mock<INetd>()
+ val interfaceTracker = mock<InterfaceTracker>()
val bpfNetMaps = mock<BpfNetMaps>().also {
doReturn(PERMISSION_INTERNET).`when`(it).getNetPermForUid(anyInt())
}
@@ -279,7 +281,11 @@
inner class CSDeps : ConnectivityService.Dependencies() {
override fun getResources(ctx: Context) = connResources
- override fun getBpfNetMaps(context: Context, netd: INetd) = this@CSTest.bpfNetMaps
+ override fun getBpfNetMaps(
+ context: Context,
+ netd: INetd,
+ interfaceTracker: InterfaceTracker
+ ) = this@CSTest.bpfNetMaps
override fun getClatCoordinator(netd: INetd?) = this@CSTest.clatCoordinator
override fun getNetworkStack() = this@CSTest.networkStack