Merge "Disable non-thread local network matching" into main am: e0051cdeb9

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/3550880

Change-Id: I5716a377ab8fc96c129d5605f984fcbc08ae5014
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot 2025-03-21 08:28:55 -07:00 • committed by Automerger Merge Worker
commit bf03e7f8c8
12 changed files with 186 additions and 39 deletions

View file

@ -17,6 +17,8 @@
package android.net;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@ -29,6 +31,7 @@ import static android.net.TetheringTester.buildIcmpEchoPacketV4;
import static android.net.TetheringTester.buildUdpPacket;
import static android.net.TetheringTester.isExpectedIcmpPacket;
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.ICMP_ECHO;
import static android.system.OsConstants.ICMP_ECHOREPLY;
@ -52,6 +55,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringTester.TetheredDevice;
@ -1244,6 +1248,11 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase {
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void testLocalAgent_networkCallbacks() throws Exception {
final boolean isMatchNonThreadLocalNetworksEnabled = runAsShell(
READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE,
() -> CompatChanges.isChangeEnabled(ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS));
assumeTrue(isMatchNonThreadLocalNetworksEnabled);
mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1");
assumeFalse(isInterfaceForTetheringAvailable());
setIncludeTestInterfaces(true);

View file

@ -360,8 +360,7 @@ public final class NetworkCapabilities implements Parcelable {
mUnderlyingNetworks = null;
mEnterpriseId = 0;
mReservationId = RES_ID_UNSET;
// TODO: Change to default disabled when introduce this filtering.
mMatchNonThreadLocalNetworks = true;
mMatchNonThreadLocalNetworks = false;
}
/**
@ -2961,8 +2960,7 @@ public final class NetworkCapabilities implements Parcelable {
* Flag to control whether a NetworkRequest can match non-thread local networks.
* @hide
*/
// TODO: Change to default disabled when introduce this filtering.
private boolean mMatchNonThreadLocalNetworks = true;
private boolean mMatchNonThreadLocalNetworks;
/**
* Returns the match non-thread local networks flag.

View file

@ -148,6 +148,17 @@ public final class ConnectivityCompatChanges {
@EnabledAfter(targetSdkVersion = 36)
public static final long RESTRICT_LOCAL_NETWORK = 365139289L;
/**
* Enable match non-threads local networks.
*
* Apps targeting a release after V can have NetworkRequests matches non-thread local networks.
*
* @hide
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public static final long ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS = 349487600L;
private ConnectivityCompatChanges() {
}
}

View file

@ -121,6 +121,7 @@ import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION;
import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION;
import static android.os.Process.INVALID_UID;
@ -1473,6 +1474,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return SdkLevel.isAtLeastV();
}
public boolean isAtLeastB() {
return SdkLevel.isAtLeastB();
}
/**
* Get system properties to use in ConnectivityService.
*/
@ -3186,6 +3191,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void maybeDisableLocalNetworkMatching(NetworkCapabilities nc, int callingUid) {
// If disabled, NetworkRequest cannot match non-thread local networks even if
// specified explicitly. Compat change is enabled by default on apps targeting B+.
// Agent should not be visible on U- even if it's rolled out.
nc.setMatchNonThreadLocalNetworks(mDeps.isAtLeastV() && mDeps.isChangeEnabled(
ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS, callingUid));
if (mDeps.isChangeEnabled(ENABLE_MATCH_LOCAL_NETWORK, callingUid)) {
return;
}

View file

@ -19,7 +19,7 @@ package com.android.server
import android.net.LocalNetworkConfig
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkCapabilities.TRANSPORT_THREAD
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
@ -42,7 +42,7 @@ class CSKeepConnectedTest : CSTest() {
fun testKeepConnectedLocalAgent() {
deps.setBuildSdk(VERSION_V)
val nc = NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_WIFI)
.addTransportType(TRANSPORT_THREAD)
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
.build()
val keepConnectedAgent = Agent(

View file

@ -18,14 +18,11 @@ package com.android.server
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.net.INetd
import android.net.LocalNetworkConfig
import android.net.NativeNetworkConfig
import android.net.NativeNetworkType
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
import android.net.VpnManager
import android.os.Build
import androidx.test.filters.SmallTest
@ -46,11 +43,6 @@ import org.mockito.Mockito.timeout
private const val TIMEOUT_MS = 2_000L
private const val NO_CALLBACK_TIMEOUT_MS = 200L
private fun keepConnectedScore() =
FromS(NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build())
private fun defaultLnc() = FromS(LocalNetworkConfig.Builder().build())
@DevSdkIgnoreRunner.MonitorThreadLeak
@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
@ -103,7 +95,7 @@ class CSLocalAgentCreationTests : CSTest() {
}
cm.registerNetworkCallback(request.build(), allNetworksCb)
val ncTemplate = NetworkCapabilities.Builder().run {
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
addCapability(NET_CAPABILITY_LOCAL_NETWORK)
}.build()
val localAgent = if (params.sdkLevel >= VERSION_V ||

View file

@ -39,6 +39,7 @@ import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
import android.net.NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK
import android.net.RouteInfo
import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK
import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS
import android.os.Build
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
@ -129,6 +130,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testStructuralConstraintViolation() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.requestNetwork(
@ -162,6 +164,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testUpdateLocalAgentConfig() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.requestNetwork(
@ -428,6 +431,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testMulticastRoutingConfig_UpstreamSelectorCellToWifi() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(
NetworkRequest.Builder().clearCapabilities()
@ -500,6 +504,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testMulticastRoutingConfig_UpstreamSelectorWifiToNull() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(
NetworkRequest.Builder().clearCapabilities()
@ -577,6 +582,7 @@ class CSLocalAgentTests : CSTest() {
fun doTestUnregisterUpstreamAfterReplacement(sameIfaceName: Boolean) {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
@ -657,6 +663,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testUnregisterUpstreamAfterReplacement_neverReplaced() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
@ -710,6 +717,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testUnregisterLocalAgentAfterReplacement() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val localCb = TestableNetworkCallback()
cm.requestNetwork(
@ -775,6 +783,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testDestroyedNetworkAsSelectedUpstream() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
@ -827,6 +836,7 @@ class CSLocalAgentTests : CSTest() {
@Test
fun testForwardingRules() {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
// Set up a local agent that should forward its traffic to the best DUN upstream.
val lnc = FromS(
LocalNetworkConfig.Builder()
@ -965,6 +975,7 @@ class CSLocalAgentTests : CSTest() {
fun doTestLocalNetworkUnwanted(haveUpstream: Boolean) {
deps.setBuildSdk(VERSION_V)
deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
val nr = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK).build()
val requestCb = TestableNetworkCallback()
@ -1075,4 +1086,86 @@ class CSLocalAgentTests : CSTest() {
expectCallback = true
)
}
@Test
fun testNonThreadLocalAgentMatches_disabled() {
doTestNonThreadLocalAgentMatches(
VERSION_V,
enableMatchNonThreadLocalNetworks = false,
TRANSPORT_WIFI,
expectAvailable = false
)
doTestNonThreadLocalAgentMatches(
VERSION_V,
enableMatchNonThreadLocalNetworks = false,
TRANSPORT_THREAD,
expectAvailable = true
)
doTestNonThreadLocalAgentMatches(
VERSION_B,
enableMatchNonThreadLocalNetworks = false,
TRANSPORT_WIFI,
expectAvailable = false
)
doTestNonThreadLocalAgentMatches(
VERSION_B,
enableMatchNonThreadLocalNetworks = false,
TRANSPORT_THREAD,
expectAvailable = true
)
}
@Test
fun testNonThreadLocalAgentMatches_enabled() {
doTestNonThreadLocalAgentMatches(
VERSION_V,
enableMatchNonThreadLocalNetworks = true,
TRANSPORT_WIFI,
expectAvailable = true
)
doTestNonThreadLocalAgentMatches(
VERSION_V,
enableMatchNonThreadLocalNetworks = true,
TRANSPORT_THREAD,
expectAvailable = true
)
doTestNonThreadLocalAgentMatches(
VERSION_B,
enableMatchNonThreadLocalNetworks = true,
TRANSPORT_WIFI,
expectAvailable = true
)
doTestNonThreadLocalAgentMatches(
VERSION_B,
enableMatchNonThreadLocalNetworks = true,
TRANSPORT_THREAD,
expectAvailable = true
)
}
private fun doTestNonThreadLocalAgentMatches(
sdkLevel: Int,
enableMatchNonThreadLocalNetworks: Boolean,
transport: Int,
expectAvailable: Boolean
) {
deps.setBuildSdk(sdkLevel)
deps.setChangeIdEnabled(
enableMatchNonThreadLocalNetworks,
ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS
)
val localNcTemplate = NetworkCapabilities.Builder().run {
addTransportType(transport)
addCapability(NET_CAPABILITY_LOCAL_NETWORK)
}.build()
val localAgent = Agent(
nc = localNcTemplate,
score = keepConnectedScore(),
lnc = defaultLnc()
)
// The implementation inside connect() will expect the OnAvailable.
localAgent.connect(expectAvailable)
localAgent.disconnect(expectAvailable)
}
}

View file

@ -20,6 +20,7 @@ import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import android.os.Process
import android.text.TextUtils
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.TestableNetworkCallback
@ -54,9 +55,17 @@ class CSNetworkRequestStateStatsMetricsTests : CSTest() {
cm.requestNetwork(CELL_INTERNET_NOT_METERED_NR, TestableNetworkCallback())
waitForIdle()
// Some fields might be altered by the service, e.g. mMatchNonThreadLocalNetworks.
// Check immutable differences instead.
verify(networkRequestStateStatsMetrics).onNetworkRequestReceived(
argThat{req -> req.networkCapabilities.equals(
CELL_INTERNET_NOT_METERED_NR.networkCapabilities)})
argThat { req ->
TextUtils.isEmpty(
req.networkCapabilities.describeImmutableDifferences(
CELL_INTERNET_NOT_METERED_NR.networkCapabilities
)
)
}
)
}
@Test
@ -77,7 +86,13 @@ class CSNetworkRequestStateStatsMetricsTests : CSTest() {
cm.unregisterNetworkCallback(cb)
waitForIdle()
verify(networkRequestStateStatsMetrics).onNetworkRequestRemoved(
argThat{req -> req.networkCapabilities.equals(
CELL_INTERNET_NOT_METERED_NR.networkCapabilities)})
argThat { req ->
TextUtils.isEmpty(
req.networkCapabilities.describeImmutableDifferences(
CELL_INTERNET_NOT_METERED_NR.networkCapabilities
)
)
}
)
}
}

View file

@ -39,6 +39,7 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_THREAD
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkPolicyManager.NetworkPolicyCallback
import android.net.NetworkRequest
@ -356,6 +357,7 @@ class CSQueuedCallbacksTest(freezingBehavior: FreezingBehavior) : CSTest() {
val localAgent = Agent(
nc = defaultNc()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
.addTransportType(TRANSPORT_THREAD)
.removeCapability(NET_CAPABILITY_INTERNET),
lp = defaultLp().apply { interfaceName = "local42" },
lnc = FromS(lnc)

View file

@ -137,7 +137,7 @@ class CSAgentWrapper(
nmCallbacks.notifyNetworkTestedWithExtras(p)
}
fun connect() {
fun connect(expectAvailable: Boolean = true) {
val mgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder().apply {
clearCapabilities()
@ -149,26 +149,30 @@ class CSAgentWrapper(
val cb = TestableNetworkCallback()
mgr.registerNetworkCallback(request, cb)
agent.markConnected()
if (null == cb.poll { it is Available && agent.network == it.network }) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
nc.hasTransport(TRANSPORT_CELLULAR)) {
// ConnectivityService adds NOT_SUSPENDED by default to all non-cell agents. An
// agent without NOT_SUSPENDED will not connect, instead going into the SUSPENDED
// state, so this call will not terminate.
// Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
// it's better to let the developer manage it as they see fit but help them
// debug if they forget.
fail(
"Could not connect the agent. Did you forget to add " +
"NET_CAPABILITY_NOT_SUSPENDED ?"
)
if (expectAvailable) {
if (null == cb.poll { it is Available && agent.network == it.network }) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
nc.hasTransport(TRANSPORT_CELLULAR)) {
// ConnectivityService adds NOT_SUSPENDED by default to all non-cell agents. An
// agent without NOT_SUSPENDED will not connect, instead going into the
// SUSPENDED state, so this call will not terminate.
// Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
// it's better to let the developer manage it as they see fit but help them
// debug if they forget.
fail(
"Could not connect the agent. Did you forget to add " +
"NET_CAPABILITY_NOT_SUSPENDED ?"
)
}
fail("Could not connect the agent. Instrumentation failure ?")
}
fail("Could not connect the agent. Instrumentation failure ?")
} else {
cb.assertNoCallback()
}
mgr.unregisterNetworkCallback(cb)
}
fun disconnect() {
fun disconnect(expectAvailable: Boolean = true) {
val mgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder().apply {
clearCapabilities()
@ -179,9 +183,14 @@ class CSAgentWrapper(
}.build()
val cb = TestableNetworkCallback(timeoutMs = SHORT_TIMEOUT_MS)
mgr.registerNetworkCallback(request, cb)
cb.eventuallyExpect<Available> { it.network == agent.network }
agent.unregister()
cb.eventuallyExpect<Lost> { it.network == agent.network }
if (expectAvailable) {
cb.eventuallyExpect<Available> { it.network == agent.network }
agent.unregister()
cb.eventuallyExpect<Lost> { it.network == agent.network }
} else {
agent.unregister()
cb.assertNoCallback()
}
}
fun setTeardownDelayMillis(delayMillis: Int) = agent.setTeardownDelayMillis(delayMillis)

View file

@ -112,7 +112,8 @@ internal const val VERSION_S = 2
internal const val VERSION_T = 3
internal const val VERSION_U = 4
internal const val VERSION_V = 5
internal const val VERSION_MAX = VERSION_V
internal const val VERSION_B = 6
internal const val VERSION_MAX = VERSION_B
internal const val CALLING_UID_UNMOCKED = Process.INVALID_UID
@ -388,6 +389,7 @@ open class CSTest {
override fun isAtLeastT() = if (isSdkUnmocked) super.isAtLeastT() else sdkLevel >= VERSION_T
override fun isAtLeastU() = if (isSdkUnmocked) super.isAtLeastU() else sdkLevel >= VERSION_U
override fun isAtLeastV() = if (isSdkUnmocked) super.isAtLeastV() else sdkLevel >= VERSION_V
override fun isAtLeastB() = if (isSdkUnmocked) super.isAtLeastB() else sdkLevel >= VERSION_B
private var callingUid = CALLING_UID_UNMOCKED

View file

@ -34,6 +34,7 @@ import android.net.INetd
import android.net.IpPrefix
import android.net.LinkAddress
import android.net.LinkProperties
import android.net.LocalNetworkConfig
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkScore
@ -84,6 +85,11 @@ internal fun defaultNc() = NetworkCapabilities.Builder()
internal fun defaultScore() = FromS(NetworkScore.Builder().build())
internal fun keepConnectedScore() = FromS(NetworkScore.Builder()
.setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_TEST).build())
internal fun defaultLnc() = FromS(LocalNetworkConfig.Builder().build())
internal fun defaultLp() = LinkProperties().apply {
addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))