Added tests for QosCallback

Tests for the newly added QosCallback functionality

Bug: 155176305
Test: Added to cts/NetworkAgentTest
Change-Id: I29769fc8be074a5105bfe4ac34e30980c5f8744a
This commit is contained in:
Remi NGUYEN VAN
2021-06-21 19:03:53 +09:00
parent 27586f8210
commit 6214f82c04

View File

@@ -29,9 +29,9 @@ import android.net.LinkProperties
import android.net.NattKeepalivePacketData import android.net.NattKeepalivePacketData
import android.net.Network import android.net.Network
import android.net.NetworkAgent import android.net.NetworkAgent
import android.net.NetworkAgentConfig
import android.net.NetworkAgent.INVALID_NETWORK import android.net.NetworkAgent.INVALID_NETWORK
import android.net.NetworkAgent.VALID_NETWORK import android.net.NetworkAgent.VALID_NETWORK
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED 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.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkInfo import android.net.NetworkInfo
import android.net.NetworkProvider import android.net.NetworkProvider
import android.net.NetworkReleasedException
import android.net.NetworkRequest import android.net.NetworkRequest
import android.net.NetworkScore import android.net.NetworkScore
import android.net.RouteInfo 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.SocketKeepalive
import android.net.Uri import android.net.Uri
import android.net.VpnManager 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.OnNetworkCreated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted 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.OnRemoveKeepalivePacketFilter
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive 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.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.Build
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
@@ -72,6 +85,7 @@ import android.os.Looper
import android.os.Message import android.os.Message
import android.os.SystemClock import android.os.SystemClock
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.telephony.data.EpsBearerQosSessionAttributes
import android.util.DebugUtils.valueToString import android.util.DebugUtils.valueToString
import androidx.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity 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.mock
import org.mockito.Mockito.timeout import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
import java.time.Duration import java.time.Duration
import java.util.Arrays import java.util.Arrays
import java.util.UUID import java.util.UUID
import java.util.concurrent.Executors
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertFalse import kotlin.test.assertFalse
@@ -143,7 +161,7 @@ class NetworkAgentTest {
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1") private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2") 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 mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
private val mFakeConnectivityService = FakeConnectivityService() private val mFakeConnectivityService = FakeConnectivityService()
@@ -152,6 +170,7 @@ class NetworkAgentTest {
private val agentsToCleanUp = mutableListOf<NetworkAgent>() private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>() private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Socket? = null
@Before @Before
fun setUp() { fun setUp() {
@@ -163,6 +182,7 @@ class NetworkAgentTest {
fun tearDown() { fun tearDown() {
agentsToCleanUp.forEach { it.unregister() } agentsToCleanUp.forEach { it.unregister() }
callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) } callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
qosTestSocket?.close()
mHandlerThread.quitSafely() mHandlerThread.quitSafely()
instrumentation.getUiAutomation().dropShellPermissionIdentity() instrumentation.getUiAutomation().dropShellPermissionIdentity()
} }
@@ -228,6 +248,11 @@ class NetworkAgentTest {
data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry() data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
object OnNetworkCreated : CallbackEntry() object OnNetworkCreated : CallbackEntry()
object OnNetworkDestroyed : CallbackEntry() object OnNetworkDestroyed : CallbackEntry()
data class OnRegisterQosCallback(
val callbackId: Int,
val filter: QosFilter
) : CallbackEntry()
data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
} }
override fun onBandwidthUpdateRequested() { 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?) { override fun onValidationStatus(status: Int, uri: Uri?) {
history.add(OnValidationStatus(status, uri)) history.add(OnValidationStatus(status, uri))
} }
@@ -307,6 +340,12 @@ class NetworkAgentTest {
return 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")
}
inline fun <reified T : CallbackEntry> eventuallyExpect() = inline fun <reified T : CallbackEntry> eventuallyExpect() =
history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also { history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
assertNotNull(it, "Callback ${T::class} not received") assertNotNull(it, "Callback ${T::class} not received")
@@ -390,7 +429,7 @@ class NetworkAgentTest {
initialConfig: NetworkAgentConfig? = null, initialConfig: NetworkAgentConfig? = null,
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf() expectedInitSignalStrengthThresholds: IntArray? = intArrayOf()
): Pair<TestableNetworkAgent, TestableNetworkCallback> { ): 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. // Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
requestNetwork(makeTestNetworkRequest(specifier = specifier), callback) requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
val agent = createNetworkAgent(context, specifier, initialConfig = initialConfig) val agent = createNetworkAgent(context, specifier, initialConfig = initialConfig)
@@ -651,7 +690,7 @@ class NetworkAgentTest {
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_VPN)) assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_VPN))
assertTrue(hasAllTransports(vpnNc, defaultNetworkTransports), assertTrue(hasAllTransports(vpnNc, defaultNetworkTransports),
"VPN transports ${Arrays.toString(vpnNc.transportTypes)}" + "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. // Check that when no underlying networks are announced the underlying transport disappears.
agent.setUnderlyingNetworks(listOf<Network>()) agent.setUnderlyingNetworks(listOf<Network>())
@@ -934,4 +973,251 @@ class NetworkAgentTest {
// tearDown() will unregister the requests and agents // 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
)
}
} }