diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2020-05-01 00:52:20 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-05-01 00:52:20 +0000 |
commit | c370cb0a230d75c24b378426e9762891b5a66c81 (patch) | |
tree | e16279851d4dc19ca3f18ab5d7039f447884e14e | |
parent | df1fcffeb6eb6db78a07edaaf1f8c3b60ac50d0f (diff) | |
parent | 9d5175317630e14c2f76de6d6f273609a46a4b76 (diff) |
Merge "Add test configuration values for probe URLs" into rvc-dev am: 9d51753176
Change-Id: Ie66ca15fcd73f83ba3336d51f3ec8700ebaf59ac
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); |