Merge "Add ConnectivityServiceIntegrationTest"
am: 324d724fd7 Change-Id: Ib82c5e5aee9e830a0efe8ea6335933237bb33856
This commit is contained in:
@@ -2165,7 +2165,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void systemReady() {
|
/**
|
||||||
|
* Called when the system is ready and ConnectivityService can initialize remaining components.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public void systemReady() {
|
||||||
mProxyTracker.loadGlobalProxy();
|
mProxyTracker.loadGlobalProxy();
|
||||||
registerNetdEventCallback();
|
registerNetdEventCallback();
|
||||||
mTethering.systemReady();
|
mTethering.systemReady();
|
||||||
|
|||||||
58
tests/net/integration/AndroidManifest.xml
Normal file
58
tests/net/integration/AndroidManifest.xml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.android.server.net.integrationtests">
|
||||||
|
|
||||||
|
<!-- For ConnectivityService registerReceiverAsUser (receiving broadcasts) -->
|
||||||
|
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||||
|
<!-- PermissionMonitor sets network permissions for each user -->
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_USERS" />
|
||||||
|
<!-- ConnectivityService sends notifications to BatteryStats -->
|
||||||
|
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
|
||||||
|
<application android:debuggable="true">
|
||||||
|
<uses-library android:name="android.test.runner" />
|
||||||
|
|
||||||
|
<!-- This manifest is merged with the base manifest of the real NetworkStack app.
|
||||||
|
Remove the NetworkStackService from the base (real) manifest, and replace with a test
|
||||||
|
service that responds to the same intent -->
|
||||||
|
<service android:name="com.android.server.NetworkStackService" tools:node="remove"/>
|
||||||
|
<service android:name=".TestNetworkStackService"
|
||||||
|
android:process="com.android.server.net.integrationtests.testnetworkstack">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.INetworkStackConnector.Test"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
<service android:name=".NetworkStackInstrumentationService"
|
||||||
|
android:process="com.android.server.net.integrationtests.testnetworkstack">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name=".INetworkStackInstrumentation"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
<service tools:replace="android:process"
|
||||||
|
android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
|
||||||
|
android:process="com.android.server.net.integrationtests.testnetworkstack"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
android:targetPackage="com.android.server.net.integrationtests"
|
||||||
|
android:label="Frameworks Net Integration Tests" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
15
tests/net/integration/res/values/config.xml
Normal file
15
tests/net/integration/res/values/config.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!--
|
||||||
|
Override configuration for testing. The below settings use the config_ variants, which are
|
||||||
|
normally used by RROs to override the setting with highest priority. -->
|
||||||
|
<integer name="config_captive_portal_dns_probe_timeout">12500</integer>
|
||||||
|
<string name="config_captive_portal_http_url" translatable="false">http://test.android.com</string>
|
||||||
|
<string name="config_captive_portal_https_url" translatable="false">https://secure.test.android.com</string>
|
||||||
|
<string-array name="config_captive_portal_fallback_urls" translatable="false">
|
||||||
|
<item>http://fallback1.android.com</item>
|
||||||
|
<item>http://fallback2.android.com</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.IBinder
|
||||||
|
import com.android.server.net.integrationtests.TestNetworkStackService
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.Mockito.timeout
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
const val TEST_ACTION_SUFFIX = ".Test"
|
||||||
|
|
||||||
|
class TestNetworkStackClient(context: Context) : NetworkStackClient(TestDependencies(context)) {
|
||||||
|
// TODO: consider switching to TrackRecord for more expressive checks
|
||||||
|
private val lastCallbacks = HashMap<Network, INetworkMonitorCallbacks>()
|
||||||
|
|
||||||
|
private class TestDependencies(private val context: Context) : Dependencies {
|
||||||
|
override fun addToServiceManager(service: IBinder) = Unit
|
||||||
|
override fun checkCallerUid() = Unit
|
||||||
|
|
||||||
|
override fun getConnectivityModuleConnector(): ConnectivityModuleConnector {
|
||||||
|
return ConnectivityModuleConnector { _, _, _, inSystemProcess ->
|
||||||
|
getNetworkStackIntent(inSystemProcess)
|
||||||
|
}.also { it.init(context) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNetworkStackIntent(inSystemProcess: Boolean): Intent? {
|
||||||
|
// Simulate out-of-system-process config: in-process service not found (null intent)
|
||||||
|
if (inSystemProcess) return null
|
||||||
|
val intent = Intent(INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX)
|
||||||
|
val serviceName = TestNetworkStackService::class.qualifiedName
|
||||||
|
?: fail("TestNetworkStackService name not found")
|
||||||
|
intent.component = ComponentName(context.packageName, serviceName)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// base may be an instance of an inaccessible subclass, so non-spyable.
|
||||||
|
// Use a known open class that delegates to the original instance for all methods except
|
||||||
|
// asBinder. asBinder needs to use its own non-delegated implementation as otherwise it would
|
||||||
|
// return a binder token to a class that is not spied on.
|
||||||
|
open class NetworkMonitorCallbacksWrapper(private val base: INetworkMonitorCallbacks) :
|
||||||
|
INetworkMonitorCallbacks.Stub(), INetworkMonitorCallbacks by base {
|
||||||
|
// asBinder is implemented by both base class and delegate: specify explicitly
|
||||||
|
override fun asBinder(): IBinder {
|
||||||
|
return super.asBinder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun makeNetworkMonitor(network: Network, name: String?, cb: INetworkMonitorCallbacks) {
|
||||||
|
val cbSpy = spy(NetworkMonitorCallbacksWrapper(cb))
|
||||||
|
lastCallbacks[network] = cbSpy
|
||||||
|
super.makeNetworkMonitor(network, name, cbSpy)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyNetworkMonitorCreated(network: Network, timeoutMs: Long) {
|
||||||
|
val cb = lastCallbacks[network]
|
||||||
|
?: fail("NetworkMonitor for network $network not requested")
|
||||||
|
verify(cb, timeout(timeoutMs)).onNetworkMonitorCreated(any())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.net.integrationtests
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Context.BIND_AUTO_CREATE
|
||||||
|
import android.content.Context.BIND_IMPORTANT
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.IDnsResolver
|
||||||
|
import android.net.INetd
|
||||||
|
import android.net.INetworkPolicyManager
|
||||||
|
import android.net.INetworkStatsService
|
||||||
|
import android.net.LinkProperties
|
||||||
|
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||||
|
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||||
|
import android.net.NetworkRequest
|
||||||
|
import android.net.TestNetworkStackClient
|
||||||
|
import android.net.metrics.IpConnectivityLog
|
||||||
|
import android.os.ConditionVariable
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.INetworkManagementService
|
||||||
|
import android.testing.TestableContext
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.android.server.ConnectivityService
|
||||||
|
import com.android.server.LocalServices
|
||||||
|
import com.android.server.NetworkAgentWrapper
|
||||||
|
import com.android.server.TestNetIdManager
|
||||||
|
import com.android.server.connectivity.DefaultNetworkMetrics
|
||||||
|
import com.android.server.connectivity.IpConnectivityMetrics
|
||||||
|
import com.android.server.connectivity.MockableSystemProperties
|
||||||
|
import com.android.server.connectivity.ProxyTracker
|
||||||
|
import com.android.server.connectivity.Tethering
|
||||||
|
import com.android.server.net.NetworkPolicyManagerInternal
|
||||||
|
import com.android.testutils.TestableNetworkCallback
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.doNothing
|
||||||
|
import org.mockito.Mockito.doReturn
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.mockito.Spy
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
const val SERVICE_BIND_TIMEOUT_MS = 5_000L
|
||||||
|
const val TEST_TIMEOUT_MS = 1_000L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that exercises an instrumented version of ConnectivityService against an instrumented
|
||||||
|
* NetworkStack in a different test process.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ConnectivityServiceIntegrationTest {
|
||||||
|
// lateinit used here for mocks as they need to be reinitialized between each test and the test
|
||||||
|
// should crash if they are used before being initialized.
|
||||||
|
@Mock
|
||||||
|
private lateinit var netManager: INetworkManagementService
|
||||||
|
@Mock
|
||||||
|
private lateinit var statsService: INetworkStatsService
|
||||||
|
@Mock
|
||||||
|
private lateinit var policyManager: INetworkPolicyManager
|
||||||
|
@Mock
|
||||||
|
private lateinit var log: IpConnectivityLog
|
||||||
|
@Mock
|
||||||
|
private lateinit var netd: INetd
|
||||||
|
@Mock
|
||||||
|
private lateinit var dnsResolver: IDnsResolver
|
||||||
|
@Mock
|
||||||
|
private lateinit var metricsLogger: IpConnectivityMetrics.Logger
|
||||||
|
@Mock
|
||||||
|
private lateinit var defaultMetrics: DefaultNetworkMetrics
|
||||||
|
@Spy
|
||||||
|
private var context = TestableContext(realContext)
|
||||||
|
|
||||||
|
// lateinit for these three classes under test, as they should be reset to a different instance
|
||||||
|
// for every test but should always be initialized before use (or the test should crash).
|
||||||
|
private lateinit var networkStackClient: TestNetworkStackClient
|
||||||
|
private lateinit var service: ConnectivityService
|
||||||
|
private lateinit var cm: ConnectivityManager
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// lateinit for this binder token, as it must be initialized before any test code is run
|
||||||
|
// and use of it before init should crash the test.
|
||||||
|
private lateinit var nsInstrumentation: INetworkStackInstrumentation
|
||||||
|
private val bindingCondition = ConditionVariable(false)
|
||||||
|
|
||||||
|
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
|
private class InstrumentationServiceConnection : ServiceConnection {
|
||||||
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
|
Log.i("TestNetworkStack", "Service connected")
|
||||||
|
try {
|
||||||
|
if (service == null) fail("Error binding to NetworkStack instrumentation")
|
||||||
|
if (::nsInstrumentation.isInitialized) fail("Service already connected")
|
||||||
|
nsInstrumentation = INetworkStackInstrumentation.Stub.asInterface(service)
|
||||||
|
} finally {
|
||||||
|
bindingCondition.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(name: ComponentName?) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setUpClass() {
|
||||||
|
val intent = Intent(realContext, NetworkStackInstrumentationService::class.java)
|
||||||
|
intent.action = INetworkStackInstrumentation::class.qualifiedName
|
||||||
|
assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(),
|
||||||
|
BIND_AUTO_CREATE or BIND_IMPORTANT),
|
||||||
|
"Error binding to instrumentation service")
|
||||||
|
assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS),
|
||||||
|
"Timed out binding to instrumentation service " +
|
||||||
|
"after $SERVICE_BIND_TIMEOUT_MS ms")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
doReturn(defaultMetrics).`when`(metricsLogger).defaultNetworkMetrics()
|
||||||
|
doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
|
||||||
|
|
||||||
|
networkStackClient = TestNetworkStackClient(realContext)
|
||||||
|
networkStackClient.init()
|
||||||
|
networkStackClient.start()
|
||||||
|
|
||||||
|
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal::class.java)
|
||||||
|
LocalServices.addService(NetworkPolicyManagerInternal::class.java,
|
||||||
|
mock(NetworkPolicyManagerInternal::class.java))
|
||||||
|
|
||||||
|
service = TestConnectivityService(makeDependencies())
|
||||||
|
cm = ConnectivityManager(context, service)
|
||||||
|
context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
|
||||||
|
|
||||||
|
service.systemReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
|
||||||
|
context, netManager, statsService, policyManager, dnsResolver, log, netd, deps)
|
||||||
|
|
||||||
|
private fun makeDependencies(): ConnectivityService.Dependencies {
|
||||||
|
val deps = spy(ConnectivityService.Dependencies())
|
||||||
|
doReturn(networkStackClient).`when`(deps).networkStack
|
||||||
|
doReturn(metricsLogger).`when`(deps).metricsLogger
|
||||||
|
doReturn(mock(Tethering::class.java)).`when`(deps).makeTethering(
|
||||||
|
any(), any(), any(), any(), any())
|
||||||
|
doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
|
||||||
|
doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
|
||||||
|
doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
|
||||||
|
return deps
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
nsInstrumentation.clearAllState()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testValidation() {
|
||||||
|
val request = NetworkRequest.Builder()
|
||||||
|
.clearCapabilities()
|
||||||
|
.addCapability(NET_CAPABILITY_INTERNET)
|
||||||
|
.build()
|
||||||
|
val testCallback = TestableNetworkCallback()
|
||||||
|
|
||||||
|
cm.registerNetworkCallback(request, testCallback)
|
||||||
|
nsInstrumentation.addHttpResponse(HttpResponse(
|
||||||
|
"http://test.android.com",
|
||||||
|
responseCode = 204, contentLength = 42, redirectUrl = null))
|
||||||
|
nsInstrumentation.addHttpResponse(HttpResponse(
|
||||||
|
"https://secure.test.android.com",
|
||||||
|
responseCode = 204, contentLength = 42, redirectUrl = null))
|
||||||
|
|
||||||
|
val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
|
||||||
|
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
|
||||||
|
|
||||||
|
na.addCapability(NET_CAPABILITY_INTERNET)
|
||||||
|
na.connect()
|
||||||
|
|
||||||
|
testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
|
||||||
|
assertEquals(2, nsInstrumentation.getRequestUrls().size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.net.integrationtests
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
data class HttpResponse(
|
||||||
|
val requestUrl: String,
|
||||||
|
val responseCode: Int,
|
||||||
|
val contentLength: Long,
|
||||||
|
val redirectUrl: String?
|
||||||
|
) : Parcelable {
|
||||||
|
constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
|
||||||
|
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
with(dest) {
|
||||||
|
writeString(requestUrl)
|
||||||
|
writeInt(responseCode)
|
||||||
|
writeLong(contentLength)
|
||||||
|
writeString(redirectUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents() = 0
|
||||||
|
companion object CREATOR : Parcelable.Creator<HttpResponse> {
|
||||||
|
override fun createFromParcel(source: Parcel) = HttpResponse(source)
|
||||||
|
override fun newArray(size: Int) = arrayOfNulls<HttpResponse?>(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.net.integrationtests
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instrumentation interface for the NetworkStack that allows controlling behavior to
|
||||||
|
* facilitate integration tests.
|
||||||
|
*/
|
||||||
|
class NetworkStackInstrumentationService : Service() {
|
||||||
|
override fun onBind(intent: Intent) = InstrumentationConnector.asBinder()
|
||||||
|
|
||||||
|
object InstrumentationConnector : INetworkStackInstrumentation.Stub() {
|
||||||
|
private val httpResponses = ConcurrentHashMap<String, ConcurrentLinkedQueue<HttpResponse>>()
|
||||||
|
.run {
|
||||||
|
withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } }
|
||||||
|
}
|
||||||
|
private val httpRequestUrls = Collections.synchronizedList(ArrayList<String>())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an HTTP request is being processed by NetworkMonitor. Returns the response
|
||||||
|
* that should be simulated.
|
||||||
|
*/
|
||||||
|
fun processRequest(url: URL): HttpResponse {
|
||||||
|
val strUrl = url.toString()
|
||||||
|
httpRequestUrls.add(strUrl)
|
||||||
|
return httpResponses[strUrl]?.poll()
|
||||||
|
?: fail("No mocked response for request: $strUrl. " +
|
||||||
|
"Mocked URL keys are: ${httpResponses.keys}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all state of this connector. This is intended for use between two tests, so all
|
||||||
|
* state should be reset as if the connector was just created.
|
||||||
|
*/
|
||||||
|
override fun clearAllState() {
|
||||||
|
httpResponses.clear()
|
||||||
|
httpRequestUrls.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a response to a future HTTP request.
|
||||||
|
*
|
||||||
|
* <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
|
||||||
|
* used to mock the query response.
|
||||||
|
*/
|
||||||
|
override fun addHttpResponse(response: HttpResponse) {
|
||||||
|
httpResponses.getValue(response.requestUrl).add(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ordered list of request URLs that have been sent by NetworkMonitor, and were
|
||||||
|
* answered based on mock responses.
|
||||||
|
*/
|
||||||
|
override fun getRequestUrls(): List<String> {
|
||||||
|
return ArrayList(httpRequestUrls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.net.integrationtests
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.INetworkMonitorCallbacks
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.metrics.IpConnectivityLog
|
||||||
|
import android.net.util.SharedLog
|
||||||
|
import android.os.IBinder
|
||||||
|
import com.android.networkstack.metrics.DataStallStatsUtils
|
||||||
|
import com.android.server.NetworkStackService.NetworkMonitorConnector
|
||||||
|
import com.android.server.NetworkStackService.NetworkStackConnector
|
||||||
|
import com.android.server.connectivity.NetworkMonitor
|
||||||
|
import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector
|
||||||
|
import org.mockito.Mockito.doReturn
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLConnection
|
||||||
|
|
||||||
|
private const val TEST_NETID = 42
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android service that can return an [android.net.INetworkStackConnector] which can be instrumented
|
||||||
|
* through [NetworkStackInstrumentationService].
|
||||||
|
* Useful in tests to create test instrumented NetworkStack components that can receive
|
||||||
|
* instrumentation commands through [NetworkStackInstrumentationService].
|
||||||
|
*/
|
||||||
|
class TestNetworkStackService : Service() {
|
||||||
|
override fun onBind(intent: Intent): IBinder = TestNetworkStackConnector(makeTestContext())
|
||||||
|
|
||||||
|
private fun makeTestContext() = spy(applicationContext).also {
|
||||||
|
doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPermissionChecker : NetworkStackConnector.PermissionChecker() {
|
||||||
|
override fun enforceNetworkStackCallingPermission() = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) :
|
||||||
|
NetworkMonitor.Dependencies() {
|
||||||
|
override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
|
||||||
|
override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class TestNetworkStackConnector(context: Context) :
|
||||||
|
NetworkStackConnector(context, TestPermissionChecker()) {
|
||||||
|
|
||||||
|
private val network = Network(TEST_NETID)
|
||||||
|
private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
|
||||||
|
|
||||||
|
private inner class TestNetwork(netId: Int) : Network(netId) {
|
||||||
|
override fun openConnection(url: URL): URLConnection {
|
||||||
|
val response = InstrumentationConnector.processRequest(url)
|
||||||
|
|
||||||
|
val connection = mock(HttpURLConnection::class.java)
|
||||||
|
doReturn(response.responseCode).`when`(connection).responseCode
|
||||||
|
doReturn(response.contentLength).`when`(connection).contentLengthLong
|
||||||
|
doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun makeNetworkMonitor(
|
||||||
|
network: Network,
|
||||||
|
name: String?,
|
||||||
|
cb: INetworkMonitorCallbacks
|
||||||
|
) {
|
||||||
|
val nm = NetworkMonitor(this@TestNetworkStackService, cb,
|
||||||
|
this.network,
|
||||||
|
mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
|
||||||
|
NetworkMonitorDeps(privateDnsBypassNetwork),
|
||||||
|
mock(DataStallStatsUtils::class.java))
|
||||||
|
cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user