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:
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user