Merge "Test races around NetworkAgent creation." into main
This commit is contained in:
commit
22bd86c584
1 changed files with 135 additions and 21 deletions
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue