Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60e502fb authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN
Browse files

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
parent 682de559
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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"/>
+59 −6
Original line number Diff line number Diff line
@@ -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))
    }
}
 No newline at end of file
+9 −4
Original line number Diff line number Diff line
@@ -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)
        }
    }
+3 −0
Original line number Diff line number Diff line
@@ -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)
+5 −1
Original line number Diff line number Diff line
@@ -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
            }
        }