Loading tests/net/integration/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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"/> Loading tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +59 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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?) { Loading Loading @@ -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) Loading @@ -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 tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt +9 −4 Original line number Diff line number Diff line Loading @@ -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) } } Loading tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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) Loading tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt +5 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } } Loading Loading
tests/net/integration/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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"/> Loading
tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +59 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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?) { Loading Loading @@ -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) Loading @@ -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
tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt +9 −4 Original line number Diff line number Diff line Loading @@ -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) } } Loading
tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt +5 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } } Loading