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

Commit 259e6f09 authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN
Browse files

Add test configuration values for probe URLs

The test configuration values override RROs that may have been set by
OEMs, which is necessary to be able to rely on them in CTS tests.

Test: atest NetworkStackTests
Bug: 152280218
Merged-In: I8171fd6360a6e504f3abaea3d7de4fa308bbb35b
Change-Id: I8171fd6360a6e504f3abaea3d7de4fa308bbb35b
parent 812baf3e
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -92,6 +92,37 @@ public class NetworkStackUtils {
     */
    public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";

    /**
     * A test URL used to override configuration settings and overlays for the network validation
     * HTTPS URL, when set in {@link android.provider.DeviceConfig} configuration.
     *
     * <p>This URL will be ignored if the host is not "localhost" (it can only be used to test with
     * a local test server), and must not be set in production scenarios (as enforced by CTS tests).
     *
     * <p>{@link #TEST_URL_EXPIRATION_TIME} must also be set to use this setting.
     */
    public static final String TEST_CAPTIVE_PORTAL_HTTPS_URL = "test_captive_portal_https_url";

    /**
     * A test URL used to override configuration settings and overlays for the network validation
     * HTTP URL, when set in {@link android.provider.DeviceConfig} configuration.
     *
     * <p>This URL will be ignored if the host is not "localhost" (it can only be used to test with
     * a local test server), and must not be set in production scenarios (as enforced by CTS tests).
     *
     * <p>{@link #TEST_URL_EXPIRATION_TIME} must also be set to use this setting.
     */
    public static final String TEST_CAPTIVE_PORTAL_HTTP_URL = "test_captive_portal_http_url";

    /**
     * Expiration time of the test URL, in ms, relative to {@link System#currentTimeMillis()}.
     *
     * <p>After this expiration time, test URLs will be ignored. They will also be ignored if
     * the expiration time is more than 10 minutes in the future, to avoid misconfiguration
     * following test runs.
     */
    public static final String TEST_URL_EXPIRATION_TIME = "test_url_expiration_time";

    /**
     * The URL used for fallback HTTP captive portal detection when previous HTTP
     * and HTTPS captive portal detection attemps did not return a conclusive answer.
+42 −0
Original line number Diff line number Diff line
@@ -71,6 +71,9 @@ import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_HTTPS_UR
import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_HTTP_URLS;
import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK;
import static android.net.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
import static android.net.util.NetworkStackUtils.TEST_URL_EXPIRATION_TIME;
import static android.net.util.NetworkStackUtils.isEmpty;
import static android.net.util.NetworkStackUtils.isIPv6ULA;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
@@ -119,6 +122,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.CellIdentityNr;
@@ -219,6 +223,7 @@ public class NetworkMonitor extends StateMachine {

    private static final int SOCKET_TIMEOUT_MS = 10000;
    private static final int PROBE_TIMEOUT_MS  = 3000;
    private static final long TEST_URL_EXPIRATION_MS = TimeUnit.MINUTES.toMillis(10);

    private static final int UNSET_MCC_OR_MNC = -1;

@@ -1598,12 +1603,47 @@ public class NetworkMonitor extends StateMachine {
        return getContextByMccMnc(Integer.parseInt(mcc), UNSET_MCC_OR_MNC);
    }

    @Nullable
    private String getTestUrl(@NonNull String key) {
        final String strExpiration = mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
                TEST_URL_EXPIRATION_TIME, null);
        if (strExpiration == null) return null;

        final long expTime;
        try {
            expTime = Long.parseUnsignedLong(strExpiration);
        } catch (NumberFormatException e) {
            loge("Invalid test URL expiration time format", e);
            return null;
        }

        final long now = System.currentTimeMillis();
        if (expTime < now || (expTime - now) > TEST_URL_EXPIRATION_MS) return null;

        return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
                key, null /* defaultValue */);
    }

    private String getCaptivePortalServerHttpsUrl() {
        final String testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
        if (isValidTestUrl(testUrl)) return testUrl;
        final Context targetContext = getCustomizedContextOrDefault();
        return getSettingFromResource(targetContext, R.string.config_captive_portal_https_url,
                R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL);
    }

    private static boolean isValidTestUrl(@Nullable String url) {
        if (TextUtils.isEmpty(url)) return false;

        try {
            // Only accept test URLs on localhost
            return Uri.parse(url).getHost().equals("localhost");
        } catch (Throwable e) {
            Log.wtf(TAG, "Error parsing test URL", e);
            return false;
        }
    }

    private int getDnsProbeTimeout() {
        return getIntSetting(mContext, R.integer.config_captive_portal_dns_probe_timeout,
                CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
@@ -1678,6 +1718,8 @@ public class NetworkMonitor extends StateMachine {
     * on one URL that can be used, while NetworkMonitor may implement more complex logic.
     */
    public String getCaptivePortalServerHttpUrl() {
        final String testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTP_URL);
        if (isValidTestUrl(testUrl)) return testUrl;
        final Context targetContext = getCustomizedContextOrDefault();
        return getSettingFromResource(targetContext, R.string.config_captive_portal_http_url,
                R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL);
+94 −0
Original line number Diff line number Diff line
@@ -40,6 +40,10 @@ import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_U
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK;
import static android.net.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
import static android.net.util.NetworkStackUtils.TEST_URL_EXPIRATION_TIME;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;

import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX;
import static com.android.server.connectivity.NetworkMonitor.extractCharset;
@@ -172,6 +176,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLHandshakeException;

@@ -203,6 +208,7 @@ public class NetworkMonitorTest {
    private @Mock HttpURLConnection mOtherHttpsConnection2;
    private @Mock HttpURLConnection mFallbackConnection;
    private @Mock HttpURLConnection mOtherFallbackConnection;
    private @Mock HttpURLConnection mTestOverriddenUrlConnection;
    private @Mock HttpURLConnection mCapportApiConnection;
    private @Mock Random mRandom;
    private @Mock NetworkMonitor.Dependencies mDependencies;
@@ -231,6 +237,8 @@ public class NetworkMonitorTest {
    private static final String TEST_HTTPS_OTHER_URL2 = "https://other2.google.com/gen_204";
    private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
    private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
    private static final String TEST_INVALID_OVERRIDE_URL = "https://override.example.com/test";
    private static final String TEST_OVERRIDE_URL = "http://localhost:12345/test";
    private static final String TEST_CAPPORT_API_URL = "https://capport.example.com/api";
    private static final String TEST_LOGIN_URL = "https://testportal.example.com/login";
    private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info";
@@ -462,6 +470,9 @@ public class NetworkMonitorTest {
                    return mFallbackConnection;
                case TEST_OTHER_FALLBACK_URL:
                    return mOtherFallbackConnection;
                case TEST_OVERRIDE_URL:
                case TEST_INVALID_OVERRIDE_URL:
                    return mTestOverriddenUrlConnection;
                case TEST_CAPPORT_API_URL:
                    return mCapportApiConnection;
                default:
@@ -1188,6 +1199,84 @@ public class NetworkMonitorTest {
        runNoValidationNetworkTest();
    }

    @Test
    public void testIsCaptivePortal_OverriddenHttpsUrlValid() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
        setStatus(mTestOverriddenUrlConnection, 204);
        setStatus(mHttpConnection, 204);

        runValidatedNetworkTest();
        verify(mHttpsConnection, never()).getResponseCode();
        verify(mTestOverriddenUrlConnection).getResponseCode();
    }

    @Test
    public void testIsCaptivePortal_OverriddenHttpUrlPortal() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
        setStatus(mHttpsConnection, 500);
        setPortal302(mTestOverriddenUrlConnection);

        runPortalNetworkTest();
        verify(mHttpConnection, never()).getResponseCode();
        verify(mTestOverriddenUrlConnection).getResponseCode();
    }

    @Test
    public void testIsCaptivePortal_InvalidHttpOverrideUrl() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_INVALID_OVERRIDE_URL);
        setStatus(mHttpsConnection, 500);
        setPortal302(mHttpConnection);

        runPortalNetworkTest();
        verify(mTestOverriddenUrlConnection, never()).getResponseCode();
        verify(mHttpConnection).getResponseCode();
    }

    @Test
    public void testIsCaptivePortal_InvalidHttpsOverrideUrl() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_INVALID_OVERRIDE_URL);
        setStatus(mHttpsConnection, 204);
        setStatus(mHttpConnection, 204);

        runValidatedNetworkTest();
        verify(mTestOverriddenUrlConnection, never()).getResponseCode();
        verify(mHttpsConnection).getResponseCode();
    }

    @Test
    public void testIsCaptivePortal_ExpiredHttpsOverrideUrl() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() - TimeUnit.MINUTES.toMillis(1)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
        setStatus(mHttpsConnection, 204);
        setStatus(mHttpConnection, 204);

        runValidatedNetworkTest();
        verify(mTestOverriddenUrlConnection, never()).getResponseCode();
        verify(mHttpsConnection).getResponseCode();
    }

    @Test
    public void testIsCaptivePortal_TestHttpUrlExpirationTooLarge() throws Exception {
        setDeviceConfig(TEST_URL_EXPIRATION_TIME,
                String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(20)));
        setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
        setStatus(mHttpsConnection, 500);
        setPortal302(mHttpConnection);

        runPortalNetworkTest();
        verify(mTestOverriddenUrlConnection, never()).getResponseCode();
        verify(mHttpConnection).getResponseCode();
    }

    @Test
    public void testIsDataStall_EvaluationDisabled() {
        setDataStallEvaluationType(0);
@@ -1985,6 +2074,11 @@ public class NetworkMonitorTest {
                eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(enabled);
    }

    private void setDeviceConfig(String key, String value) {
        doReturn(value).when(mDependencies).getDeviceConfigProperty(eq(NAMESPACE_CONNECTIVITY),
                eq(key), any() /* defaultValue */);
    }

    private NetworkMonitor runPortalNetworkTest() throws RemoteException {
        final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PORTAL,
                0 /* probesSucceeded */, TEST_LOGIN_URL);