Merge "Add configurable captive portal probes" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1f8f21af60
@@ -237,6 +237,14 @@ public class ConnectivityManager {
|
||||
*/
|
||||
public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
|
||||
|
||||
/**
|
||||
* Key for passing a {@link android.net.captiveportal.CaptivePortalProbeSpec} to the captive
|
||||
* portal login activity.
|
||||
* {@hide}
|
||||
*/
|
||||
public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC =
|
||||
"android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
|
||||
|
||||
/**
|
||||
* Key for passing a user agent string to the captive portal login activity.
|
||||
* {@hide}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.net.captiveportal;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Result of calling isCaptivePortal().
|
||||
* @hide
|
||||
@@ -23,6 +25,7 @@ package android.net.captiveportal;
|
||||
public final class CaptivePortalProbeResult {
|
||||
public static final int SUCCESS_CODE = 204;
|
||||
public static final int FAILED_CODE = 599;
|
||||
public static final int PORTAL_CODE = 302;
|
||||
|
||||
public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE);
|
||||
public static final CaptivePortalProbeResult SUCCESS =
|
||||
@@ -32,15 +35,23 @@ public final class CaptivePortalProbeResult {
|
||||
public final String redirectUrl; // Redirect destination returned from Internet probe.
|
||||
public final String detectUrl; // URL where a 204 response code indicates
|
||||
// captive portal has been appeased.
|
||||
@Nullable
|
||||
public final CaptivePortalProbeSpec probeSpec;
|
||||
|
||||
public CaptivePortalProbeResult(int httpResponseCode) {
|
||||
this(httpResponseCode, null, null);
|
||||
}
|
||||
|
||||
public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl) {
|
||||
this(httpResponseCode, redirectUrl, detectUrl, null);
|
||||
}
|
||||
|
||||
public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl, String detectUrl,
|
||||
CaptivePortalProbeSpec probeSpec) {
|
||||
mHttpResponseCode = httpResponseCode;
|
||||
this.redirectUrl = redirectUrl;
|
||||
this.detectUrl = detectUrl;
|
||||
this.probeSpec = probeSpec;
|
||||
}
|
||||
|
||||
public boolean isSuccessful() {
|
||||
|
||||
180
core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
Normal file
180
core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.captiveportal;
|
||||
|
||||
import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE;
|
||||
import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
/** @hide */
|
||||
public abstract class CaptivePortalProbeSpec {
|
||||
public static final String HTTP_LOCATION_HEADER_NAME = "Location";
|
||||
|
||||
private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName();
|
||||
private static final String REGEX_SEPARATOR = "@@/@@";
|
||||
private static final String SPEC_SEPARATOR = "@@,@@";
|
||||
|
||||
private final String mEncodedSpec;
|
||||
private final URL mUrl;
|
||||
|
||||
CaptivePortalProbeSpec(String encodedSpec, URL url) {
|
||||
mEncodedSpec = encodedSpec;
|
||||
mUrl = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a {@link CaptivePortalProbeSpec} from a {@link String}.
|
||||
*
|
||||
* <p>The valid format is a URL followed by two regular expressions, each separated by "@@/@@".
|
||||
* @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}.
|
||||
* @throws ParseException The string is empty, does not match the above format, or a regular
|
||||
* expression is invalid for {@link Pattern#compile(String)}.
|
||||
*/
|
||||
@NonNull
|
||||
public static CaptivePortalProbeSpec parseSpec(String spec) throws ParseException,
|
||||
MalformedURLException {
|
||||
if (TextUtils.isEmpty(spec)) {
|
||||
throw new ParseException("Empty probe spec", 0 /* errorOffset */);
|
||||
}
|
||||
|
||||
String[] splits = TextUtils.split(spec, REGEX_SEPARATOR);
|
||||
if (splits.length != 3) {
|
||||
throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */);
|
||||
}
|
||||
|
||||
final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length();
|
||||
final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length();
|
||||
final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos);
|
||||
final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos);
|
||||
|
||||
return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pattern parsePatternIfNonEmpty(String pattern, int pos) throws ParseException {
|
||||
if (TextUtils.isEmpty(pattern)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Pattern.compile(pattern);
|
||||
} catch (PatternSyntaxException e) {
|
||||
throw new ParseException(
|
||||
String.format("Invalid status pattern [%s]: %s", pattern, e),
|
||||
pos /* errorOffset */);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec
|
||||
* based on the status code of the provided URL if the spec cannot be parsed.
|
||||
*/
|
||||
@Nullable
|
||||
public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) {
|
||||
if (spec != null) {
|
||||
try {
|
||||
return parseSpec(spec);
|
||||
} catch (ParseException | MalformedURLException e) {
|
||||
Log.e(TAG, "Invalid probe spec: " + spec, e);
|
||||
// Fall through
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a config String to build an array of {@link CaptivePortalProbeSpec}.
|
||||
*
|
||||
* <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}.
|
||||
* <p>This method does not throw but ignores any entry that could not be parsed.
|
||||
*/
|
||||
public static CaptivePortalProbeSpec[] parseCaptivePortalProbeSpecs(String settingsVal) {
|
||||
List<CaptivePortalProbeSpec> specs = new ArrayList<>();
|
||||
if (settingsVal != null) {
|
||||
for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) {
|
||||
try {
|
||||
specs.add(parseSpec(spec));
|
||||
} catch (ParseException | MalformedURLException e) {
|
||||
Log.e(TAG, "Invalid probe spec: " + spec, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (specs.isEmpty()) {
|
||||
Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal));
|
||||
}
|
||||
return specs.toArray(new CaptivePortalProbeSpec[specs.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the probe result from HTTP status and location header.
|
||||
*/
|
||||
public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader);
|
||||
|
||||
public String getEncodedSpec() {
|
||||
return mEncodedSpec;
|
||||
}
|
||||
|
||||
public URL getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular
|
||||
* expressions for the HTTP status code and location header (if any). Matches indicate that
|
||||
* the page is not a portal.
|
||||
* This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE
|
||||
*/
|
||||
private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec {
|
||||
@Nullable
|
||||
final Pattern mStatusRegex;
|
||||
@Nullable
|
||||
final Pattern mLocationHeaderRegex;
|
||||
|
||||
RegexMatchProbeSpec(
|
||||
String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) {
|
||||
super(spec, url);
|
||||
mStatusRegex = statusRegex;
|
||||
mLocationHeaderRegex = locationHeaderRegex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptivePortalProbeResult getResult(int status, String locationHeader) {
|
||||
final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex);
|
||||
final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex);
|
||||
final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE;
|
||||
return new CaptivePortalProbeResult(
|
||||
returnCode, locationHeader, getUrl().toString(), this);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) {
|
||||
// No value is a match ("no location header" passes the location rule for non-redirects)
|
||||
return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches();
|
||||
}
|
||||
}
|
||||
@@ -10281,6 +10281,15 @@ public final class Settings {
|
||||
public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
|
||||
"captive_portal_other_fallback_urls";
|
||||
|
||||
/**
|
||||
* A list of captive portal detection specifications used in addition to the fallback URLs.
|
||||
* Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated
|
||||
* by "@@,@@".
|
||||
* @hide
|
||||
*/
|
||||
public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
|
||||
"captive_portal_fallback_probe_specs";
|
||||
|
||||
/**
|
||||
* Whether to use HTTPS for network validation. This is enabled by default and the setting
|
||||
* needs to be set to 0 to disable it. This setting is a misnomer because captive portals
|
||||
|
||||
@@ -156,6 +156,7 @@ public class SettingsBackupTest {
|
||||
Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
|
||||
Settings.Global.CAPTIVE_PORTAL_MODE,
|
||||
Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
|
||||
Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS,
|
||||
Settings.Global.CAPTIVE_PORTAL_SERVER,
|
||||
Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
|
||||
Settings.Global.CAPTIVE_PORTAL_USER_AGENT,
|
||||
|
||||
@@ -19,7 +19,11 @@ package com.android.server.connectivity;
|
||||
import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
|
||||
import static android.net.CaptivePortal.APP_RETURN_UNWANTED;
|
||||
import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
|
||||
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
|
||||
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
|
||||
import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -35,6 +39,7 @@ import android.net.ProxyInfo;
|
||||
import android.net.TrafficStats;
|
||||
import android.net.Uri;
|
||||
import android.net.captiveportal.CaptivePortalProbeResult;
|
||||
import android.net.captiveportal.CaptivePortalProbeSpec;
|
||||
import android.net.dns.ResolvUtil;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.metrics.NetworkEvent;
|
||||
@@ -63,6 +68,7 @@ import android.util.LocalLog.ReadOnlyLocalLog;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.Protocol;
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.StateMachine;
|
||||
@@ -259,6 +265,8 @@ public class NetworkMonitor extends StateMachine {
|
||||
private final URL mCaptivePortalHttpsUrl;
|
||||
private final URL mCaptivePortalHttpUrl;
|
||||
private final URL[] mCaptivePortalFallbackUrls;
|
||||
@Nullable
|
||||
private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs;
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean mIsCaptivePortalCheckEnabled;
|
||||
@@ -334,6 +342,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
|
||||
mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
|
||||
mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
|
||||
mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
|
||||
|
||||
start();
|
||||
}
|
||||
@@ -542,8 +551,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
|
||||
}
|
||||
}));
|
||||
intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL,
|
||||
mLastPortalProbeResult.detectUrl);
|
||||
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
|
||||
intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
|
||||
if (probeRes.probeSpec != null) {
|
||||
final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
|
||||
intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
|
||||
}
|
||||
intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
|
||||
mCaptivePortalUserAgent);
|
||||
intent.setFlags(
|
||||
@@ -883,23 +896,47 @@ public class NetworkMonitor extends StateMachine {
|
||||
}
|
||||
|
||||
private URL[] makeCaptivePortalFallbackUrls() {
|
||||
String separator = ",";
|
||||
String firstUrl = mSettings.getSetting(mContext,
|
||||
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
|
||||
String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
|
||||
Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, DEFAULT_OTHER_FALLBACK_URLS);
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (String s : joinedUrls.split(separator)) {
|
||||
URL u = makeURL(s);
|
||||
if (u == null) {
|
||||
continue;
|
||||
try {
|
||||
String separator = ",";
|
||||
String firstUrl = mSettings.getSetting(mContext,
|
||||
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
|
||||
String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
|
||||
Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
|
||||
DEFAULT_OTHER_FALLBACK_URLS);
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (String s : joinedUrls.split(separator)) {
|
||||
URL u = makeURL(s);
|
||||
if (u == null) {
|
||||
continue;
|
||||
}
|
||||
urls.add(u);
|
||||
}
|
||||
urls.add(u);
|
||||
if (urls.isEmpty()) {
|
||||
Log.e(TAG, String.format("could not create any url from %s", joinedUrls));
|
||||
}
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
} catch (Exception e) {
|
||||
// Don't let a misconfiguration bootloop the system.
|
||||
Log.e(TAG, "Error parsing configured fallback URLs", e);
|
||||
return new URL[0];
|
||||
}
|
||||
if (urls.isEmpty()) {
|
||||
Log.e(TAG, String.format("could not create any url from %s", joinedUrls));
|
||||
}
|
||||
|
||||
private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() {
|
||||
try {
|
||||
final String settingsValue = mSettings.getSetting(
|
||||
mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
|
||||
// Probe specs only used if configured in settings
|
||||
if (TextUtils.isEmpty(settingsValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue);
|
||||
} catch (Exception e) {
|
||||
// Don't let a misconfiguration bootloop the system.
|
||||
Log.e(TAG, "Error parsing configured fallback probe specs", e);
|
||||
return null;
|
||||
}
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
|
||||
private String getCaptivePortalUserAgent() {
|
||||
@@ -916,6 +953,15 @@ public class NetworkMonitor extends StateMachine {
|
||||
return mCaptivePortalFallbackUrls[idx];
|
||||
}
|
||||
|
||||
private CaptivePortalProbeSpec nextFallbackSpec() {
|
||||
if (ArrayUtils.isEmpty(mCaptivePortalFallbackSpecs)) {
|
||||
return null;
|
||||
}
|
||||
// Randomly change spec without memory. Also randomize the first attempt.
|
||||
final int idx = Math.abs(new Random().nextInt()) % mCaptivePortalFallbackSpecs.length;
|
||||
return mCaptivePortalFallbackSpecs[idx];
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected CaptivePortalProbeResult isCaptivePortal() {
|
||||
if (!mIsCaptivePortalCheckEnabled) {
|
||||
@@ -986,7 +1032,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
// unnecessary resolution.
|
||||
final String host = (proxy != null) ? proxy.getHost() : url.getHost();
|
||||
sendDnsProbe(host);
|
||||
return sendHttpProbe(url, probeType);
|
||||
return sendHttpProbe(url, probeType, null);
|
||||
}
|
||||
|
||||
/** Do a DNS resolution of the given server. */
|
||||
@@ -1022,7 +1068,8 @@ public class NetworkMonitor extends StateMachine {
|
||||
* @return a CaptivePortalProbeResult inferred from the HTTP response.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType) {
|
||||
protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType,
|
||||
@Nullable CaptivePortalProbeSpec probeSpec) {
|
||||
HttpURLConnection urlConnection = null;
|
||||
int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
|
||||
String redirectUrl = null;
|
||||
@@ -1094,7 +1141,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
TrafficStats.setThreadStatsTag(oldTag);
|
||||
}
|
||||
logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
|
||||
return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
|
||||
|
||||
if (probeSpec == null) {
|
||||
return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
|
||||
} else {
|
||||
return probeSpec.getResult(httpResponseCode, redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private CaptivePortalProbeResult sendParallelHttpProbes(
|
||||
@@ -1157,11 +1209,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
if (httpsResult.isPortal() || httpsResult.isSuccessful()) {
|
||||
return httpsResult;
|
||||
}
|
||||
// If a fallback url exists, use a fallback probe to try again portal detection.
|
||||
URL fallbackUrl = nextFallbackUrl();
|
||||
// If a fallback method exists, use it to retry portal detection.
|
||||
// If we have new-style probe specs, use those. Otherwise, use the fallback URLs.
|
||||
final CaptivePortalProbeSpec probeSpec = nextFallbackSpec();
|
||||
final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl();
|
||||
if (fallbackUrl != null) {
|
||||
CaptivePortalProbeResult result =
|
||||
sendHttpProbe(fallbackUrl, ValidationProbeEvent.PROBE_FALLBACK);
|
||||
CaptivePortalProbeResult result = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
|
||||
if (result.isPortal()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.captiveportal;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CaptivePortalProbeSpecTest {
|
||||
|
||||
@Test
|
||||
public void testGetResult_Regex() throws MalformedURLException, ParseException {
|
||||
// 2xx status or 404, with an empty (match everything) location regex
|
||||
CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec(
|
||||
"http://www.google.com@@/@@2[0-9]{2}|404@@/@@");
|
||||
|
||||
// 404, or 301/302 redirect to some HTTPS page under google.com
|
||||
CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec(
|
||||
"http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*");
|
||||
|
||||
assertSuccess(statusRegexSpec.getResult(200, null));
|
||||
assertSuccess(statusRegexSpec.getResult(299, "qwer"));
|
||||
assertSuccess(statusRegexSpec.getResult(404, null));
|
||||
assertSuccess(statusRegexSpec.getResult(404, ""));
|
||||
|
||||
assertPortal(statusRegexSpec.getResult(300, null));
|
||||
assertPortal(statusRegexSpec.getResult(399, "qwer"));
|
||||
assertPortal(statusRegexSpec.getResult(500, null));
|
||||
|
||||
assertSuccess(redirectSpec.getResult(404, null));
|
||||
assertSuccess(redirectSpec.getResult(404, ""));
|
||||
assertSuccess(redirectSpec.getResult(301, "https://www.google.com"));
|
||||
assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3"));
|
||||
assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3"));
|
||||
|
||||
assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3"));
|
||||
assertPortal(redirectSpec.getResult(299, ""));
|
||||
assertPortal(redirectSpec.getResult(499, null));
|
||||
assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage"));
|
||||
assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3"));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_Empty() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_Null() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec(null);
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_MissingParts() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket");
|
||||
}
|
||||
|
||||
@Test(expected = MalformedURLException.class)
|
||||
public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testParseSpec_NoParts() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("invalid");
|
||||
}
|
||||
|
||||
@Test(expected = MalformedURLException.class)
|
||||
public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException {
|
||||
CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSpecOrNull_UsesSpec() {
|
||||
final String specUrl = "http://google.com/probe";
|
||||
final String redirectUrl = "https://google.com/probe";
|
||||
CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(
|
||||
specUrl + "@@/@@302@@/@@" + redirectUrl);
|
||||
assertEquals(specUrl, spec.getUrl().toString());
|
||||
|
||||
assertPortal(spec.getResult(302, "http://portal.example.com"));
|
||||
assertSuccess(spec.getResult(302, redirectUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSpecOrNull_UsesFallback() throws MalformedURLException {
|
||||
CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null);
|
||||
assertNull(spec);
|
||||
|
||||
spec = CaptivePortalProbeSpec.parseSpecOrNull("");
|
||||
assertNull(spec);
|
||||
|
||||
spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@");
|
||||
assertNull(spec);
|
||||
|
||||
spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456");
|
||||
assertNull(spec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException {
|
||||
CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull("");
|
||||
assertNull(spec);
|
||||
}
|
||||
|
||||
private void assertIsStatusSpec(CaptivePortalProbeSpec spec) {
|
||||
assertSuccess(spec.getResult(204, null));
|
||||
assertSuccess(spec.getResult(204, "1234"));
|
||||
|
||||
assertPortal(spec.getResult(200, null));
|
||||
assertPortal(spec.getResult(301, null));
|
||||
assertPortal(spec.getResult(302, "1234"));
|
||||
assertPortal(spec.getResult(399, ""));
|
||||
|
||||
assertFailed(spec.getResult(404, null));
|
||||
assertFailed(spec.getResult(500, "1234"));
|
||||
}
|
||||
|
||||
private void assertPortal(CaptivePortalProbeResult result) {
|
||||
assertTrue(result.isPortal());
|
||||
}
|
||||
|
||||
private void assertSuccess(CaptivePortalProbeResult result) {
|
||||
assertTrue(result.isSuccessful());
|
||||
}
|
||||
|
||||
private void assertFailed(CaptivePortalProbeResult result) {
|
||||
assertTrue(result.isFailed());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user