Merge "Test races around NetworkAgent creation." into main

This commit is contained in:
Treehugger Robot 2024-10-31 15:41:15 +00:00 • committed by Gerrit Code Review
commit 22bd86c584

View file

@ -83,13 +83,17 @@ import android.os.Build
import android.os.ConditionVariable
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.os.PersistableBundle
import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
import android.system.Os
import android.system.OsConstants.AF_INET6
import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
@ -105,6 +109,10 @@ import com.android.compatibility.common.util.ThrowingSupplier
import com.android.compatibility.common.util.UiccUtil
import com.android.modules.utils.build.SdkLevel
import com.android.net.module.util.ArrayTrackRecord
import com.android.net.module.util.NetworkStackConstants.ETHER_MTU
import com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN
import com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET
import com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN
import com.android.testutils.CompatUtil
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@ -115,6 +123,7 @@ import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.PollPacketReader
import com.android.testutils.TestableNetworkAgent
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
@ -133,6 +142,7 @@ import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
import com.android.testutils.waitForIdle
import java.io.Closeable
import java.io.IOException
import java.net.DatagramSocket
@ -140,10 +150,13 @@ import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
import java.security.MessageDigest
import java.nio.ByteBuffer
import java.time.Duration
import java.util.Arrays
import java.util.Random
import java.util.UUID
import java.util.concurrent.Executors
import kotlin.collections.ArrayList
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
@ -188,6 +201,11 @@ private fun Message(what: Int, arg1: Int, arg2: Int, obj: Any?) = Message.obtain
it.obj = obj
}
private val LINK_ADDRESS = LinkAddress("2001:db8::1/64")
private val REMOTE_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::123")
private val PREFIX = IpPrefix("2001:db8::/64")
private val NEXTHOP = InetAddresses.parseNumericAddress("fe80::abcd")
// On T and below, the native network is only created when the agent connects.
// Starting in U, the native network was to be created as soon as the agent is registered,
// but this has been flagged off for now pending resolution of race conditions.
@ -321,6 +339,15 @@ class NetworkAgentTest {
if (transports.size > 0) removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
}
private fun makeTestLinkProperties(ifName: String): LinkProperties {
return LinkProperties().apply {
interfaceName = ifName
addLinkAddress(LINK_ADDRESS)
addRoute(RouteInfo(PREFIX, null /* nextHop */, ifName))
addRoute(RouteInfo(IpPrefix("::/0"), NEXTHOP, ifName))
}
}
private fun createNetworkAgent(
context: Context = realContext,
specifier: String? = null,
@ -341,6 +368,7 @@ class NetworkAgentTest {
private fun createConnectedNetworkAgent(
context: Context = realContext,
lp: LinkProperties? = null,
specifier: String? = UUID.randomUUID().toString(),
initialConfig: NetworkAgentConfig? = null,
expectedInitSignalStrengthThresholds: IntArray = intArrayOf(),
@ -350,7 +378,8 @@ class NetworkAgentTest {
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
requestNetwork(makeTestNetworkRequest(specifier), callback)
val nc = makeTestNetworkCapabilities(specifier, transports)
val agent = createNetworkAgent(context, initialConfig = initialConfig, initialNc = nc)
val agent = createNetworkAgent(context, initialConfig = initialConfig, initialLp = lp,
initialNc = nc)
agent.setTeardownDelayMillis(0)
// Connect the agent and verify initial status callbacks.
agent.register()
@ -361,8 +390,9 @@ class NetworkAgentTest {
return agent to callback
}
private fun connectNetwork(vararg transports: Int): Pair<TestableNetworkAgent, Network> {
val (agent, callback) = createConnectedNetworkAgent(transports = transports)
private fun connectNetwork(vararg transports: Int, lp: LinkProperties? = null):
Pair<TestableNetworkAgent, Network> {
val (agent, callback) = createConnectedNetworkAgent(transports = transports, lp = lp)
val network = agent.network!!
// createConnectedNetworkAgent internally files a request; release it so that the network
// will be torn down if unneeded.
@ -382,8 +412,9 @@ class NetworkAgentTest {
assertNoCallback()
}
private fun createTunInterface(): TestNetworkInterface = realContext.getSystemService(
TestNetworkManager::class.java)!!.createTunInterface(emptyList()).also {
private fun createTunInterface(addrs: Collection<LinkAddress> = emptyList()):
TestNetworkInterface = realContext.getSystemService(
TestNetworkManager::class.java)!!.createTunInterface(addrs).also {
ifacesToCleanUp.add(it)
}
@ -1501,15 +1532,75 @@ class NetworkAgentTest {
private fun createEpsAttributes(qci: Int = 1): EpsBearerQosSessionAttributes {
val remoteAddresses = ArrayList<InetSocketAddress>()
remoteAddresses.add(InetSocketAddress("2001:db8::123", 80))
remoteAddresses.add(InetSocketAddress(REMOTE_ADDRESS, 80))
return EpsBearerQosSessionAttributes(
qci, 2, 3, 4, 5,
remoteAddresses
)
}
fun sendAndExpectUdpPacket(net: Network,
reader: PollPacketReader, iface: TestNetworkInterface) {
val s = Os.socket(AF_INET6, SOCK_DGRAM, 0)
net.bindSocket(s)
val content = ByteArray(16)
Random().nextBytes(content)
Os.sendto(s, ByteBuffer.wrap(content), 0, REMOTE_ADDRESS, 7 /* port */)
val match = reader.poll(DEFAULT_TIMEOUT_MS) {
val udpStart = IPV6_HEADER_LEN + UDP_HEADER_LEN
it.size == udpStart + content.size &&
it[0].toInt() and 0xf0 == 0x60 &&
it[IPV6_PROTOCOL_OFFSET].toInt() == IPPROTO_UDP &&
Arrays.equals(content, it.copyOfRange(udpStart, udpStart + content.size))
}
assertNotNull(match, "Did not receive matching packet on ${iface.interfaceName} " +
" after ${DEFAULT_TIMEOUT_MS}ms")
}
fun createInterfaceAndReader(): Triple<TestNetworkInterface, PollPacketReader, LinkProperties> {
val iface = createTunInterface(listOf(LINK_ADDRESS))
val handler = Handler(Looper.getMainLooper())
val reader = PollPacketReader(handler, iface.fileDescriptor.fileDescriptor, ETHER_MTU)
reader.startAsyncForTest()
handler.waitForIdle(DEFAULT_TIMEOUT_MS)
val ifName = iface.interfaceName
val lp = makeTestLinkProperties(ifName)
return Triple(iface, reader, lp)
}
@Test
fun testRegisterAfterUnregister() {
val (iface, reader, lp) = createInterfaceAndReader()
// File a request that matches and keeps up the best-scoring test network.
val testCallback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
requestNetwork(makeTestNetworkRequest(), testCallback)
// Register and unregister networkagents in a loop, checking that every time an agent
// connects, the native network is correctly configured and packets can be sent.
// Running 10 iterations takes about 1 second on x86 cuttlefish, and detects the race in
// b/286649301 most of the time.
for (i in 1..10) {
val agent1 = createNetworkAgent(realContext, initialLp = lp)
agent1.register()
agent1.unregister()
val agent2 = createNetworkAgent(realContext, initialLp = lp)
agent2.register()
agent2.markConnected()
val network2 = agent2.network!!
testCallback.expectAvailableThenValidatedCallbacks(network2)
sendAndExpectUdpPacket(network2, reader, iface)
agent2.unregister()
testCallback.expect<Lost>(network2)
}
}
@Test
fun testUnregisterAfterReplacement() {
val (iface, reader, lp) = createInterfaceAndReader()
// Keeps an eye on all test networks.
val matchAllCallback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
registerNetworkCallback(makeTestNetworkRequest(), matchAllCallback)
@ -1519,14 +1610,13 @@ class NetworkAgentTest {
requestNetwork(makeTestNetworkRequest(), testCallback)
// Connect the first network. This should satisfy the request.
val (agent1, network1) = connectNetwork()
val (agent1, network1) = connectNetwork(lp = lp)
matchAllCallback.expectAvailableThenValidatedCallbacks(network1)
testCallback.expectAvailableThenValidatedCallbacks(network1)
// Check that network1 exists by binding a socket to it and getting no exceptions.
network1.bindSocket(DatagramSocket())
sendAndExpectUdpPacket(network1, reader, iface)
// Connect a second agent. network1 is preferred because it was already registered, so
// testCallback will not see any events. agent2 is be torn down because it has no requests.
// testCallback will not see any events. agent2 is torn down because it has no requests.
val (agent2, network2) = connectNetwork()
matchAllCallback.expectAvailableThenValidatedCallbacks(network2)
matchAllCallback.expect<Lost>(network2)
@ -1551,9 +1641,10 @@ class NetworkAgentTest {
// as soon as it validates (until then, it is outscored by network1).
// The fact that the first events seen by matchAllCallback is the connection of network3
// implicitly ensures that no callbacks are sent since network1 was lost.
val (agent3, network3) = connectNetwork()
val (agent3, network3) = connectNetwork(lp = lp)
matchAllCallback.expectAvailableThenValidatedCallbacks(network3)
testCallback.expectAvailableDoubleValidatedCallbacks(network3)
sendAndExpectUdpPacket(network3, reader, iface)
// As soon as the replacement arrives, network1 is disconnected.
// Check that this happens before the replacement timeout (5 seconds) fires.
@ -1573,6 +1664,7 @@ class NetworkAgentTest {
matchAllCallback.expect<Losing>(network3)
testCallback.expectAvailableCallbacks(network4, validated = true)
mCM.unregisterNetworkCallback(agent4callback)
sendAndExpectUdpPacket(network3, reader, iface)
agent3.unregisterAfterReplacement(5_000)
agent3.expectCallback<OnNetworkUnwanted>()
matchAllCallback.expect<Lost>(network3, 1000L)
@ -1588,9 +1680,10 @@ class NetworkAgentTest {
// If a network that is awaiting replacement is unregistered, it disconnects immediately,
// before the replacement timeout fires.
val (agent5, network5) = connectNetwork()
val (agent5, network5) = connectNetwork(lp = lp)
matchAllCallback.expectAvailableThenValidatedCallbacks(network5)
testCallback.expectAvailableThenValidatedCallbacks(network5)
sendAndExpectUdpPacket(network5, reader, iface)
agent5.unregisterAfterReplacement(5_000 /* timeoutMillis */)
agent5.unregister()
matchAllCallback.expect<Lost>(network5, 1000L /* timeoutMs */)
@ -1637,7 +1730,7 @@ class NetworkAgentTest {
matchAllCallback.assertNoCallback(200 /* timeoutMs */)
// If wifi is replaced within the timeout, the device does not switch to cellular.
val (_, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
val (cellAgent, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
testCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
matchAllCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
@ -1674,6 +1767,34 @@ class NetworkAgentTest {
matchAllCallback.expectAvailableThenValidatedCallbacks(newWifiNetwork)
matchAllCallback.expect<Lost>(wifiNetwork)
wifiAgent.expectCallback<OnNetworkUnwanted>()
testCallback.expect<CapabilitiesChanged>(newWifiNetwork)
cellAgent.unregister()
matchAllCallback.expect<Lost>(cellNetwork)
newWifiAgent.unregister()
matchAllCallback.expect<Lost>(newWifiNetwork)
testCallback.expect<Lost>(newWifiNetwork)
// Calling unregisterAfterReplacement several times in quick succession works.
// These networks are all kept up by testCallback.
val agent10 = createNetworkAgent(realContext, initialLp = lp)
agent10.register()
agent10.unregisterAfterReplacement(5_000)
val agent11 = createNetworkAgent(realContext, initialLp = lp)
agent11.register()
agent11.unregisterAfterReplacement(5_000)
val agent12 = createNetworkAgent(realContext, initialLp = lp)
agent12.register()
agent12.unregisterAfterReplacement(5_000)
val agent13 = createNetworkAgent(realContext, initialLp = lp)
agent13.register()
agent13.markConnected()
testCallback.expectAvailableThenValidatedCallbacks(agent13.network!!)
sendAndExpectUdpPacket(agent13.network!!, reader, iface)
agent13.unregister()
}
@Test
@ -1706,14 +1827,7 @@ class NetworkAgentTest {
it.underlyingNetworks = listOf()
}
}
val lp = LinkProperties().apply {
interfaceName = ifName
addLinkAddress(LinkAddress("2001:db8::1/64"))
addRoute(RouteInfo(IpPrefix("2001:db8::/64"), null /* nextHop */, ifName))
addRoute(RouteInfo(IpPrefix("::/0"),
InetAddresses.parseNumericAddress("fe80::abcd"),
ifName))
}
val lp = makeTestLinkProperties(ifName)
// File a request containing the agent's specifier to receive callbacks and to ensure that
// the agent is not torn down due to being unneeded.