diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2020-04-28 06:16:55 +0000 |
---|---|---|
committer | Remi NGUYEN VAN <reminv@google.com> | 2020-04-30 06:20:20 +0000 |
commit | 259e6f09d2bb13c71f5bd2b868318b2cf4fd5d3d (patch) | |
tree | a89c72b205122512542bb18ef1f309a02a8fdb39 | |
parent | 812baf3e3a5bd0a7cfdaf4b96c8ae190a36b7d30 (diff) |
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
3 files changed, 167 insertions, 0 deletions
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java index 6fd6043..be4c2e4 100755 --- a/src/android/net/util/NetworkStackUtils.java +++ b/src/android/net/util/NetworkStackUtils.java @@ -93,6 +93,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. */ diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index b6387ab..4c3c4bd 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java @@ -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); diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 145c89c..7cf130e 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -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: @@ -1189,6 +1200,84 @@ public class NetworkMonitorTest { } @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); WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); @@ -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); |