Added tests for QosCallback

Tests for the newly added QosCallback functionality

Bug: 155176305
Test: Added to cts/NetworkAgentTest
Original-Change: https://android-review.googlesource.com/1558648
Merged-In: I29769fc8be074a5105bfe4ac34e30980c5f8744a
Change-Id: I29769fc8be074a5105bfe4ac34e30980c5f8744a
This commit is contained in:
Remi NGUYEN VAN
2021-06-23 05:50:54 +00:00
parent 3674367bcc
commit 763f3a0fb3

View File

@@ -29,9 +29,9 @@ import android.net.LinkProperties
import android.net.NattKeepalivePacketData
import android.net.Network
import android.net.NetworkAgent
import android.net.NetworkAgentConfig
import android.net.NetworkAgent.INVALID_NETWORK
import android.net.NetworkAgent.VALID_NETWORK
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
@@ -46,9 +46,17 @@ import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkInfo
import android.net.NetworkProvider
import android.net.NetworkReleasedException
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.RouteInfo
import android.net.QosCallback
import android.net.QosCallbackException
import android.net.QosCallback.QosCallbackRegistrationException
import android.net.QosFilter
import android.net.QosSession
import android.net.QosSessionAttributes
import android.net.QosSocketInfo
import android.net.SocketKeepalive
import android.net.Uri
import android.net.VpnManager
@@ -59,12 +67,17 @@ import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBan
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
@@ -72,6 +85,7 @@ import android.os.Looper
import android.os.Message
import android.os.SystemClock
import android.telephony.TelephonyManager
import android.telephony.data.EpsBearerQosSessionAttributes
import android.util.DebugUtils.valueToString
import androidx.test.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
@@ -97,9 +111,13 @@ import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
import java.time.Duration
import java.util.Arrays
import java.util.UUID
import java.util.concurrent.Executors
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
@@ -143,7 +161,7 @@ class NetworkAgentTest {
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2")
private val mCM = realContext.getSystemService(ConnectivityManager::class.java)
private val mCM = realContext.getSystemService(ConnectivityManager::class.java)!!
private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
private val mFakeConnectivityService = FakeConnectivityService()
@@ -152,6 +170,7 @@ class NetworkAgentTest {
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Socket? = null
@Before
fun setUp() {
@@ -163,6 +182,7 @@ class NetworkAgentTest {
fun tearDown() {
agentsToCleanUp.forEach { it.unregister() }
callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
qosTestSocket?.close()
mHandlerThread.quitSafely()
instrumentation.getUiAutomation().dropShellPermissionIdentity()
}
@@ -228,6 +248,11 @@ class NetworkAgentTest {
data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
object OnNetworkCreated : CallbackEntry()
object OnNetworkDestroyed : CallbackEntry()
data class OnRegisterQosCallback(
val callbackId: Int,
val filter: QosFilter
) : CallbackEntry()
data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
}
override fun onBandwidthUpdateRequested() {
@@ -276,6 +301,14 @@ class NetworkAgentTest {
}
}
override fun onQosCallbackRegistered(qosCallbackId: Int, filter: QosFilter) {
history.add(OnRegisterQosCallback(qosCallbackId, filter))
}
override fun onQosCallbackUnregistered(qosCallbackId: Int) {
history.add(OnUnregisterQosCallback(qosCallbackId))
}
override fun onValidationStatus(status: Int, uri: Uri?) {
history.add(OnValidationStatus(status, uri))
}
@@ -307,6 +340,12 @@ class NetworkAgentTest {
return foundCallback
}
inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
}
inline fun <reified T : CallbackEntry> eventuallyExpect() =
history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
assertNotNull(it, "Callback ${T::class} not received")
@@ -390,7 +429,7 @@ class NetworkAgentTest {
initialConfig: NetworkAgentConfig? = null,
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf()
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
val callback = TestableNetworkCallback()
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
val agent = createNetworkAgent(context, specifier, initialConfig = initialConfig)
@@ -651,7 +690,7 @@ class NetworkAgentTest {
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_VPN))
assertTrue(hasAllTransports(vpnNc, defaultNetworkTransports),
"VPN transports ${Arrays.toString(vpnNc.transportTypes)}" +
" lacking transports from ${Arrays.toString(defaultNetworkTransports)}")
" lacking transports from ${Arrays.toString(defaultNetworkTransports)}")
// Check that when no underlying networks are announced the underlying transport disappears.
agent.setUnderlyingNetworks(listOf<Network>())
@@ -934,4 +973,251 @@ class NetworkAgentTest {
// tearDown() will unregister the requests and agents
}
private class TestableQosCallback : QosCallback() {
val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
sealed class CallbackEntry {
data class OnQosSessionAvailable(val sess: QosSession, val attr: QosSessionAttributes)
: CallbackEntry()
data class OnQosSessionLost(val sess: QosSession)
: CallbackEntry()
data class OnError(val ex: QosCallbackException)
: CallbackEntry()
}
override fun onQosSessionAvailable(sess: QosSession, attr: QosSessionAttributes) {
history.add(OnQosSessionAvailable(sess, attr))
}
override fun onQosSessionLost(sess: QosSession) {
history.add(OnQosSessionLost(sess))
}
override fun onError(ex: QosCallbackException) {
history.add(OnError(ex))
}
inline fun <reified T : CallbackEntry> expectCallback(): T {
val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
return foundCallback
}
inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
}
fun assertNoCallback() {
assertNull(history.poll(NO_CALLBACK_TIMEOUT),
"Callback received")
}
}
private fun setupForQosCallbackTesting(): Pair<TestableNetworkAgent, Socket> {
val request = NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
.build()
val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
requestNetwork(request, callback)
val (agent, _) = createConnectedNetworkAgent()
qosTestSocket = assertNotNull(agent.network?.socketFactory?.createSocket()).also {
it.bind(InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
}
return Pair(agent, qosTestSocket!!)
}
@Test
fun testQosCallbackRegisterWithUnregister() {
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
var callbackId = -1
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
mCM.registerQosCallback(info, executor, qosCallback)
callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
assertFailsWith<QosCallbackRegistrationException>(
"The same callback cannot be " +
"registered more than once without first being unregistered") {
mCM.registerQosCallback(info, executor, qosCallback)
}
} finally {
socket.close()
mCM.unregisterQosCallback(qosCallback)
agent.expectCallback<OnUnregisterQosCallback> { it.callbackId == callbackId }
executor.shutdown()
}
}
}
@Test
fun testQosCallbackOnQosSession() {
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
mCM.registerQosCallback(info, executor, qosCallback)
val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
val uniqueSessionId = 4294967397
val sessId = 101
val attributes = createEpsAttributes(5)
assertEquals(attributes.qosIdentifier, 5)
agent.sendQosSessionAvailable(callbackId, sessId, attributes)
qosCallback.expectCallback<OnQosSessionAvailable> {
it.sess.sessionId == sessId && it.sess.uniqueId == uniqueSessionId &&
it.sess.sessionType == QosSession.TYPE_EPS_BEARER
}
agent.sendQosSessionLost(callbackId, sessId, QosSession.TYPE_EPS_BEARER)
qosCallback.expectCallback<OnQosSessionLost> {
it.sess.sessionId == sessId && it.sess.uniqueId == uniqueSessionId &&
it.sess.sessionType == QosSession.TYPE_EPS_BEARER
}
// Make sure that we don't get more qos callbacks
mCM.unregisterQosCallback(qosCallback)
agent.expectCallback<OnUnregisterQosCallback>()
agent.sendQosSessionLost(callbackId, sessId, QosSession.TYPE_EPS_BEARER)
qosCallback.assertNoCallback()
} finally {
socket.close()
// safety precaution
mCM.unregisterQosCallback(qosCallback)
executor.shutdown()
}
}
}
@Test
fun testQosCallbackOnError() {
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
mCM.registerQosCallback(info, executor, qosCallback)
val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
val sessId = 101
val attributes = createEpsAttributes()
// Double check that this is wired up and ready to go
agent.sendQosSessionAvailable(callbackId, sessId, attributes)
qosCallback.expectCallback<OnQosSessionAvailable>()
// Check that onError is coming through correctly
agent.sendQosCallbackError(callbackId,
QosCallbackException.EX_TYPE_FILTER_NOT_SUPPORTED)
qosCallback.expectCallback<OnError> {
it.ex.cause is UnsupportedOperationException
}
// Ensure that when an error occurs the callback was also unregistered
agent.sendQosSessionLost(callbackId, sessId, QosSession.TYPE_EPS_BEARER)
qosCallback.assertNoCallback()
} finally {
socket.close()
// Make sure that the callback is fully unregistered
mCM.unregisterQosCallback(qosCallback)
executor.shutdown()
}
}
}
@Test
fun testQosCallbackIdsAreMappedCorrectly() {
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback1 = TestableQosCallback()
val qosCallback2 = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
mCM.registerQosCallback(info, executor, qosCallback1)
val callbackId1 = agent.expectCallback<OnRegisterQosCallback>().callbackId
mCM.registerQosCallback(info, executor, qosCallback2)
val callbackId2 = agent.expectCallback<OnRegisterQosCallback>().callbackId
val sessId1 = 101
val attributes1 = createEpsAttributes(1)
// Check #1
agent.sendQosSessionAvailable(callbackId1, sessId1, attributes1)
qosCallback1.expectCallback<OnQosSessionAvailable>()
qosCallback2.assertNoCallback()
// Check #2
val sessId2 = 102
val attributes2 = createEpsAttributes(2)
agent.sendQosSessionAvailable(callbackId2, sessId2, attributes2)
qosCallback1.assertNoCallback()
qosCallback2.expectCallback<OnQosSessionAvailable> { sessId2 == it.sess.sessionId }
} finally {
socket.close()
// Make sure that the callback is fully unregistered
mCM.unregisterQosCallback(qosCallback1)
mCM.unregisterQosCallback(qosCallback2)
executor.shutdown()
}
}
}
@Test
fun testQosCallbackWhenNetworkReleased() {
val (agent, socket) = setupForQosCallbackTesting()
Executors.newSingleThreadExecutor().let { executor ->
try {
val qosCallback1 = TestableQosCallback()
val qosCallback2 = TestableQosCallback()
try {
val info = QosSocketInfo(agent.network!!, socket)
mCM.registerQosCallback(info, executor, qosCallback1)
mCM.registerQosCallback(info, executor, qosCallback2)
agent.unregister()
qosCallback1.expectCallback<OnError> {
it.ex.cause is NetworkReleasedException
}
qosCallback2.expectCallback<OnError> {
it.ex.cause is NetworkReleasedException
}
} finally {
socket.close()
mCM.unregisterQosCallback(qosCallback1)
mCM.unregisterQosCallback(qosCallback2)
}
} finally {
socket.close()
executor.shutdown()
}
}
}
private fun createEpsAttributes(qci: Int = 1): EpsBearerQosSessionAttributes {
val remoteAddresses = ArrayList<InetSocketAddress>()
remoteAddresses.add(InetSocketAddress("2001:db8::123", 80))
return EpsBearerQosSessionAttributes(
qci, 2, 3, 4, 5,
remoteAddresses
)
}
}