From 28c0533bf0d30c1c9c281b81be90fa8ee013a396 Mon Sep 17 00:00:00 2001 From: Sarup Dalwani Date: Fri, 14 Mar 2025 17:01:29 +0000 Subject: [PATCH] Removing interfaces from the InterfaceTracker when network is destroyed. Bug: 400681214 Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivity.InterfaceTrackerTest Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivityservice.CSInterfaceTrackerTest Change-Id: Ifb3388800dec6ed208ca28dba080c5b736f09d35 --- .../android/server/ConnectivityService.java | 4 + .../server/connectivity/InterfaceTracker.java | 2 + .../CSInterfaceTrackerTest.kt | 96 +++++++++++++++++++ .../server/connectivityservice/base/CSTest.kt | 2 + 4 files changed, 104 insertions(+) create mode 100644 tests/unit/java/com/android/server/connectivityservice/CSInterfaceTrackerTest.kt diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 2c44b629a9..45b431f1e7 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -5787,6 +5787,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { mDeps.disableIngressRateLimit(nai.linkProperties.getInterfaceName()); } + // Removes the interfaces associated with the network being destroyed from the tracker. + for (String interfaceName : nai.linkProperties.getAllInterfaceNames()) { + mInterfaceTracker.removeInterface(interfaceName); + } nai.setDestroyed(); nai.onNetworkDestroyed(); } diff --git a/service/src/com/android/server/connectivity/InterfaceTracker.java b/service/src/com/android/server/connectivity/InterfaceTracker.java index 0b4abeb9cc..c6686afcc2 100644 --- a/service/src/com/android/server/connectivity/InterfaceTracker.java +++ b/service/src/com/android/server/connectivity/InterfaceTracker.java @@ -22,6 +22,7 @@ import android.system.Os; import android.util.ArrayMap; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.BpfNetMaps; @@ -39,6 +40,7 @@ public class InterfaceTracker { } private static final String TAG = "InterfaceTracker"; private final Dependencies mDeps; + @GuardedBy("mInterfaceMap") private final Map mInterfaceMap; public InterfaceTracker(final Context context) { diff --git a/tests/unit/java/com/android/server/connectivityservice/CSInterfaceTrackerTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSInterfaceTrackerTest.kt new file mode 100644 index 0000000000..b06e11357f --- /dev/null +++ b/tests/unit/java/com/android/server/connectivityservice/CSInterfaceTrackerTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.connectivityservice + +import android.net.IpPrefix +import android.net.LinkAddress +import android.net.LinkProperties +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN +import android.net.NetworkCapabilities.TRANSPORT_VPN +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.net.NetworkRequest +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.server.CSTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.RecorderCallback.CallbackEntry.Lost +import com.android.testutils.TestableNetworkCallback +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.inOrder +import org.mockito.Mockito.verify + +private const val WIFI_IFNAME = "wlan0" + +private val wifiNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + +private fun lp(iface: String, vararg linkAddresses: LinkAddress) = LinkProperties().apply { + interfaceName = iface + for (linkAddress in linkAddresses) { + addLinkAddress(linkAddress) + } +} + +private fun nr(transport: Int) = NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(transport).apply { + if (transport != TRANSPORT_VPN) { + addCapability(NET_CAPABILITY_NOT_VPN) + } + }.build() + +@DevSdkIgnoreRunner.MonitorThreadLeak +@RunWith(DevSdkIgnoreRunner::class) +@SmallTest +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +class CSInterfaceTrackerTest : CSTest() { + private val LOCAL_IPV6_IP_ADDRESS_PREFIX = IpPrefix("fe80::1cf1:35ff:fe8c:db87/64") + private val LOCAL_IPV6_LINK_ADDRESS = LinkAddress( + LOCAL_IPV6_IP_ADDRESS_PREFIX.getAddress(), + LOCAL_IPV6_IP_ADDRESS_PREFIX.getPrefixLength() + ) + + @Test + fun testDisconnectingNetwork_InterfaceRemoved() { + val nr = nr(NetworkCapabilities.TRANSPORT_WIFI) + val cb = TestableNetworkCallback() + val interfaceTrackerInorder = inOrder(interfaceTracker) + cm.requestNetwork(nr, cb) + + // Connecting to network with IPv6 local address in LinkProperties + val wifiLp = lp(WIFI_IFNAME, LOCAL_IPV6_LINK_ADDRESS) + val wifiAgent = Agent(nc = wifiNc, lp = wifiLp) + wifiAgent.connect() + cb.expectAvailableCallbacks(wifiAgent.network, validated = false) + interfaceTrackerInorder.verify(interfaceTracker).addInterface(WIFI_IFNAME) + + wifiAgent.disconnect() + cb.expect(timeoutMs = 500) { it.network == wifiAgent.network } + // onLost is fired before the network is destroyed. + waitForIdle() + + interfaceTrackerInorder.verify(interfaceTracker).removeInterface(WIFI_IFNAME) + } +} 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 0fe61eca44..5c55483ee9 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt @@ -288,6 +288,8 @@ open class CSTest { netd: INetd, interfaceTracker: InterfaceTracker ) = this@CSTest.bpfNetMaps + + override fun getInterfaceTracker(context: Context?) = this@CSTest.interfaceTracker override fun getClatCoordinator(netd: INetd?) = this@CSTest.clatCoordinator override fun getNetworkStack() = this@CSTest.networkStack