Add integration test for capport API
Add a test to verify the ConnectivityService / NetworkMonitor integration around the captive portal API. Test: atest ConnectivityServiceIntegrationTest Bug: 156062304 Change-Id: I4eed02e09fc4943c011d871c58ba97ec572c7763
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.NETWORK_FACTORY"/>
|
||||
<!-- Obtain LinkProperties callbacks with sensitive fields -->
|
||||
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.NETWORK_STACK"/>
|
||||
<uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
|
||||
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
|
||||
|
||||
@@ -28,10 +28,13 @@ import android.net.INetd
|
||||
import android.net.INetworkPolicyManager
|
||||
import android.net.INetworkStatsService
|
||||
import android.net.LinkProperties
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.net.NetworkRequest
|
||||
import android.net.TestNetworkStackClient
|
||||
import android.net.Uri
|
||||
import android.net.metrics.IpConnectivityLog
|
||||
import android.os.ConditionVariable
|
||||
import android.os.IBinder
|
||||
@@ -64,6 +67,8 @@ import org.mockito.Mockito.spy
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.Spy
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
@@ -110,6 +115,10 @@ class ConnectivityServiceIntegrationTest {
|
||||
private val bindingCondition = ConditionVariable(false)
|
||||
|
||||
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
|
||||
private val httpProbeUrl get() =
|
||||
realContext.getResources().getString(R.string.config_captive_portal_http_url)
|
||||
private val httpsProbeUrl get() =
|
||||
realContext.getResources().getString(R.string.config_captive_portal_https_url)
|
||||
|
||||
private class InstrumentationServiceConnection : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
@@ -188,12 +197,8 @@ class ConnectivityServiceIntegrationTest {
|
||||
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))
|
||||
nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
|
||||
nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
|
||||
|
||||
val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
|
||||
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
|
||||
@@ -204,4 +209,52 @@ class ConnectivityServiceIntegrationTest {
|
||||
testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
|
||||
assertEquals(2, nsInstrumentation.getRequestUrls().size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCapportApi() {
|
||||
val request = NetworkRequest.Builder()
|
||||
.clearCapabilities()
|
||||
.addCapability(NET_CAPABILITY_INTERNET)
|
||||
.build()
|
||||
val testCb = TestableNetworkCallback()
|
||||
val apiUrl = "https://capport.android.com"
|
||||
|
||||
cm.registerNetworkCallback(request, testCb)
|
||||
nsInstrumentation.addHttpResponse(HttpResponse(
|
||||
apiUrl,
|
||||
"""
|
||||
|{
|
||||
| "captive": true,
|
||||
| "user-portal-url": "https://login.capport.android.com",
|
||||
| "venue-info-url": "https://venueinfo.capport.android.com"
|
||||
|}
|
||||
""".trimMargin()))
|
||||
|
||||
// Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the
|
||||
// HTTP probe as it should not be sent.
|
||||
// Even if the HTTPS probe succeeds, a portal should be detected as the API takes precedence
|
||||
// in that case.
|
||||
nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
|
||||
|
||||
val lp = LinkProperties()
|
||||
lp.captivePortalApiUrl = Uri.parse(apiUrl)
|
||||
val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
|
||||
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
|
||||
|
||||
na.addCapability(NET_CAPABILITY_INTERNET)
|
||||
na.connect()
|
||||
|
||||
testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS)
|
||||
|
||||
val capportData = testCb.expectLinkPropertiesThat(na, TEST_TIMEOUT_MS) {
|
||||
it.captivePortalData != null
|
||||
}.lp.captivePortalData
|
||||
assertNotNull(capportData)
|
||||
assertTrue(capportData.isCaptive)
|
||||
assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
|
||||
assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl)
|
||||
|
||||
val nc = testCb.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, na, TEST_TIMEOUT_MS)
|
||||
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED))
|
||||
}
|
||||
}
|
||||
@@ -22,16 +22,21 @@ import android.os.Parcelable
|
||||
data class HttpResponse(
|
||||
val requestUrl: String,
|
||||
val responseCode: Int,
|
||||
val contentLength: Long,
|
||||
val redirectUrl: String?
|
||||
val content: String = "",
|
||||
val redirectUrl: String? = null
|
||||
) : Parcelable {
|
||||
constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
|
||||
constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString())
|
||||
constructor(requestUrl: String, contentBody: String): this(
|
||||
requestUrl,
|
||||
responseCode = 200,
|
||||
content = contentBody,
|
||||
redirectUrl = null)
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
with(dest) {
|
||||
writeString(requestUrl)
|
||||
writeInt(responseCode)
|
||||
writeLong(contentLength)
|
||||
writeString(content)
|
||||
writeString(redirectUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ class NetworkStackInstrumentationService : Service() {
|
||||
*
|
||||
* <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
|
||||
* used to mock the query response.
|
||||
*
|
||||
* <p>All requests that are expected to be sent must have a mock response: if an unexpected
|
||||
* request is seen, the test will fail.
|
||||
*/
|
||||
override fun addHttpResponse(response: HttpResponse) {
|
||||
httpResponses.getValue(response.requestUrl).add(response)
|
||||
|
||||
@@ -33,9 +33,11 @@ import com.android.server.net.integrationtests.NetworkStackInstrumentationServic
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.spy
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URLConnection
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
private const val TEST_NETID = 42
|
||||
|
||||
@@ -71,11 +73,13 @@ class TestNetworkStackService : Service() {
|
||||
private inner class TestNetwork(netId: Int) : Network(netId) {
|
||||
override fun openConnection(url: URL): URLConnection {
|
||||
val response = InstrumentationConnector.processRequest(url)
|
||||
val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
|
||||
|
||||
val connection = mock(HttpURLConnection::class.java)
|
||||
doReturn(response.responseCode).`when`(connection).responseCode
|
||||
doReturn(response.contentLength).`when`(connection).contentLengthLong
|
||||
doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong
|
||||
doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
|
||||
doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream
|
||||
return connection
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user