Loading apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java +5 −0 Original line number Diff line number Diff line Loading @@ -37,4 +37,9 @@ public class ConstantsShim extends com.android.networkstack.apishim.api29.Consta DataStallReport.DETECTION_METHOD_DNS_EVENTS; public static final int DETECTION_METHOD_TCP_METRICS = DataStallReport.DETECTION_METHOD_TCP_METRICS; /** * @see android.net.NetworkCapabilities */ public static final int TRANSPORT_TEST = 7; } apishim/31/com/android/networkstack/apishim/ConstantsShim.java +4 −0 Original line number Diff line number Diff line Loading @@ -30,4 +30,8 @@ public class ConstantsShim extends com.android.networkstack.apishim.api30.Consta */ @VisibleForTesting public static final int VERSION = 31; // When removing this shim, the version in NetworkMonitorUtils should be removed too. // TODO: add TRANSPORT_TEST to system API in API 31 (it is only a test API as of R) public static final int TRANSPORT_TEST = 7; } common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java +35 −3 Original line number Diff line number Diff line Loading @@ -20,11 +20,21 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import android.net.NetworkCapabilities; /** @hide */ public class NetworkMonitorUtils { // This class is used by both NetworkMonitor and ConnectivityService, so it cannot use // NetworkStack shims, but at the same time cannot use non-system APIs. // TRANSPORT_TEST is test API as of R (so it is enforced to always be 7 and can't be changed), // and it is being added as a system API in S. // TODO: use NetworkCapabilities.TRANSPORT_TEST once NetworkStack builds against API 31. private static final int TRANSPORT_TEST = 7; // Network conditions broadcast constants public static final String ACTION_NETWORK_CONDITIONS_MEASURED = Loading @@ -47,11 +57,33 @@ public class NetworkMonitorUtils { * @param nc Network capabilities of the network to test. */ public static boolean isPrivateDnsValidationRequired(NetworkCapabilities nc) { if (nc == null) return false; // TODO: Consider requiring validation for DUN networks. return nc != null && nc.hasCapability(NET_CAPABILITY_INTERNET) if (nc.hasCapability(NET_CAPABILITY_INTERNET) && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) && nc.hasCapability(NET_CAPABILITY_TRUSTED); && nc.hasCapability(NET_CAPABILITY_TRUSTED)) { // Real networks return true; } // TODO: once TRANSPORT_TEST is @SystemApi in S and S SDK is stable (so constant shims can // be replaced with the SDK constant that will be inlined), replace isTestNetwork with // hasTransport(TRANSPORT_TEST) // Test networks that also have one of the major transport types are attempting to replicate // that transport on a test interface (for example, test ethernet networks with // EthernetManager#setIncludeTestInterfaces). Run validation on them for realistic tests. // See also comments on EthernetManager#setIncludeTestInterfaces and on TestNetworkManager. if (nc.hasTransport(TRANSPORT_TEST) && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) && ( nc.hasTransport(TRANSPORT_WIFI) || nc.hasTransport(TRANSPORT_CELLULAR) || nc.hasTransport(TRANSPORT_BLUETOOTH) || nc.hasTransport(TRANSPORT_ETHERNET))) { return true; } return false; } /** Loading src/android/net/ip/IpClient.java +17 −3 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; import android.util.Patterns; import android.util.SparseArray; import androidx.annotation.NonNull; Loading @@ -86,6 +85,8 @@ import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; Loading Loading @@ -1263,9 +1264,9 @@ public class IpClient extends StateMachine { } final String capportUrl = mDhcpResults.captivePortalApiUrl; // Uri.parse does no syntax check; do a simple regex check to eliminate garbage. // Uri.parse does no syntax check; do a simple check to eliminate garbage. // If the URL is still incorrect data fetching will fail later, which is fine. if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) { if (isParseableUrl(capportUrl)) { NetworkInformationShimImpl.newInstance() .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl)); } Loading Loading @@ -1303,6 +1304,19 @@ public class IpClient extends StateMachine { return newLp; } private static boolean isParseableUrl(String url) { // Verify that a URL has a reasonable format that can be parsed as per the URL constructor. // This does not use Patterns.WEB_URL as that pattern excludes URLs without TLDs, such as on // localhost. if (url == null) return false; try { new URL(url); return true; } catch (MalformedURLException e) { return false; } } private static void addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses) { // TODO: Investigate deleting this reachability check. We should be Loading src/com/android/server/connectivity/NetworkMonitor.java +55 −26 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_DNS_EVENTS; import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_TCP_METRICS; import static com.android.networkstack.apishim.ConstantsShim.TRANSPORT_TEST; import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX; import static com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG; Loading Loading @@ -387,7 +388,8 @@ public class NetworkMonitor extends StateMachine { private static final int CMD_BANDWIDTH_CHECK_TIMEOUT = 24; // Start mReevaluateDelayMs at this value and double. private static final int INITIAL_REEVALUATE_DELAY_MS = 1000; @VisibleForTesting static final int INITIAL_REEVALUATE_DELAY_MS = 1000; private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000; // Default timeout of evaluating network bandwidth. private static final int DEFAULT_EVALUATING_BANDWIDTH_TIMEOUT_MS = 10_000; Loading Loading @@ -1090,7 +1092,7 @@ public class NetworkMonitor extends StateMachine { final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; // Use redirect URL from AP if exists. final String portalUrl = (useRedirectUrlForPortal() && probeRes.redirectUrl != null) (useRedirectUrlForPortal() && makeURL(probeRes.redirectUrl) != null) ? probeRes.redirectUrl : probeRes.detectUrl; appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, portalUrl); if (probeRes.probeSpec != null) { Loading Loading @@ -1434,8 +1436,9 @@ public class NetworkMonitor extends StateMachine { } final int token = ++mProbeToken; final EvaluationThreadDeps deps = new EvaluationThreadDeps(mNetworkCapabilities); mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0, isCaptivePortal()))); isCaptivePortal(deps)))); mThread.start(); } Loading Loading @@ -2147,8 +2150,25 @@ public class NetworkMonitor extends StateMachine { return mCaptivePortalFallbackSpecs[idx]; } @VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { /** * Parameters that can be accessed by the evaluation thread in a thread-safe way. * * Parameters such as LinkProperties and NetworkCapabilities cannot be accessed by the * evaluation thread directly, as they are managed in the state machine thread and not * synchronized. This class provides a copy of the required data that is not modified and can be * used safely by the evaluation thread. */ private static class EvaluationThreadDeps { // TODO: add parameters that are accessed in a non-thread-safe way from the evaluation // thread (read from LinkProperties, NetworkCapabilities, useHttps, validationStage) private final boolean mIsTestNetwork; EvaluationThreadDeps(NetworkCapabilities nc) { this.mIsTestNetwork = nc.hasTransport(TRANSPORT_TEST); } } private CaptivePortalProbeResult isCaptivePortal(EvaluationThreadDeps deps) { if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return CaptivePortalProbeResult.success(CaptivePortalProbeResult.PROBE_UNKNOWN); Loading Loading @@ -2196,11 +2216,11 @@ public class NetworkMonitor extends StateMachine { reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); } else if (mUseHttps && httpsUrls.length == 1 && httpUrls.length == 1) { // Probe results are reported inside sendHttpAndHttpsParallelWithFallbackProbes. result = sendHttpAndHttpsParallelWithFallbackProbes( proxyInfo, httpsUrls[0], httpUrls[0]); result = sendHttpAndHttpsParallelWithFallbackProbes(deps, proxyInfo, httpsUrls[0], httpUrls[0]); } else if (mUseHttps) { // Support result aggregation from multiple Urls. result = sendMultiParallelHttpAndHttpsProbes(proxyInfo, httpsUrls, httpUrls); result = sendMultiParallelHttpAndHttpsProbes(deps, proxyInfo, httpsUrls, httpUrls); } else { result = sendDnsAndHttpProbes(proxyInfo, httpUrls[0], ValidationProbeEvent.PROBE_HTTP); reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); Loading Loading @@ -2482,12 +2502,12 @@ public class NetworkMonitor extends StateMachine { private final CountDownLatch mLatch; private final Probe mProbe; ProbeThread(CountDownLatch latch, ProxyInfo proxy, URL url, int probeType, Uri captivePortalApiUrl) { ProbeThread(CountDownLatch latch, EvaluationThreadDeps deps, ProxyInfo proxy, URL url, int probeType, Uri captivePortalApiUrl) { mLatch = latch; mProbe = (probeType == ValidationProbeEvent.PROBE_HTTPS) ? new HttpsProbe(proxy, url, captivePortalApiUrl) : new HttpProbe(proxy, url, captivePortalApiUrl); ? new HttpsProbe(deps, proxy, url, captivePortalApiUrl) : new HttpProbe(deps, proxy, url, captivePortalApiUrl); mResult = CaptivePortalProbeResult.failed(probeType); } Loading @@ -2512,11 +2532,14 @@ public class NetworkMonitor extends StateMachine { } private abstract static class Probe { protected final EvaluationThreadDeps mDeps; protected final ProxyInfo mProxy; protected final URL mUrl; protected final Uri mCaptivePortalApiUrl; protected Probe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { protected Probe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { mDeps = deps; mProxy = proxy; mUrl = url; mCaptivePortalApiUrl = captivePortalApiUrl; Loading @@ -2526,8 +2549,8 @@ public class NetworkMonitor extends StateMachine { } final class HttpsProbe extends Probe { HttpsProbe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(proxy, url, captivePortalApiUrl); HttpsProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(deps, proxy, url, captivePortalApiUrl); } @Override Loading @@ -2537,8 +2560,8 @@ public class NetworkMonitor extends StateMachine { } final class HttpProbe extends Probe { HttpProbe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(proxy, url, captivePortalApiUrl); HttpProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(deps, proxy, url, captivePortalApiUrl); } private CaptivePortalDataShim tryCapportApiProbe() { Loading @@ -2548,7 +2571,12 @@ public class NetworkMonitor extends StateMachine { final String apiContent; try { final URL url = new URL(mCaptivePortalApiUrl.toString()); if (!"https".equals(url.getProtocol())) { // Protocol must be HTTPS // (as per https://www.ietf.org/id/draft-ietf-capport-api-07.txt, #4). // Only allow HTTP on localhost, for testing. final boolean isTestLocalhostHttp = mDeps.mIsTestNetwork && "localhost".equals(url.getHost()) && "http".equals(url.getProtocol()); if (!"https".equals(url.getProtocol()) && !isTestLocalhostHttp) { validationLog("Invalid captive portal API protocol: " + url.getProtocol()); return null; } Loading Loading @@ -2631,8 +2659,9 @@ public class NetworkMonitor extends StateMachine { && captivePortalApiUrl == null); } private CaptivePortalProbeResult sendMultiParallelHttpAndHttpsProbes(@NonNull ProxyInfo proxy, @NonNull URL[] httpsUrls, @NonNull URL[] httpUrls) { private CaptivePortalProbeResult sendMultiParallelHttpAndHttpsProbes( @NonNull EvaluationThreadDeps deps, @Nullable ProxyInfo proxy, @NonNull URL[] httpsUrls, @NonNull URL[] httpUrls) { // If multiple URLs are required to ensure the correctness of validation, send parallel // probes to explore the result in separate probe threads and aggregate those results into // one as the final result for either HTTP or HTTPS. Loading @@ -2657,13 +2686,13 @@ public class NetworkMonitor extends StateMachine { // TODO: Have the capport probe as a different probe for cleanliness. final URL urlMaybeWithCapport = httpUrls[0]; for (final URL url : httpUrls) { futures.add(ecs.submit(() -> new HttpProbe(proxy, url, futures.add(ecs.submit(() -> new HttpProbe(deps, proxy, url, url.equals(urlMaybeWithCapport) ? capportApiUrl : null).sendProbe())); } for (final URL url : httpsUrls) { futures.add( ecs.submit(() -> new HttpsProbe(proxy, url, capportApiUrl).sendProbe())); futures.add(ecs.submit(() -> new HttpsProbe(deps, proxy, url, capportApiUrl) .sendProbe())); } final ArrayList<CaptivePortalProbeResult> completedProbes = new ArrayList<>(); Loading Loading @@ -2759,15 +2788,15 @@ public class NetworkMonitor extends StateMachine { } private CaptivePortalProbeResult sendHttpAndHttpsParallelWithFallbackProbes( ProxyInfo proxy, URL httpsUrl, URL httpUrl) { EvaluationThreadDeps deps, ProxyInfo proxy, URL httpsUrl, URL httpUrl) { // Number of probes to wait for. If a probe completes with a conclusive answer // it shortcuts the latch immediately by forcing the count to 0. final CountDownLatch latch = new CountDownLatch(2); final Uri capportApiUrl = getCaptivePortalApiUrl(mLinkProperties); final ProbeThread httpsProbe = new ProbeThread(latch, proxy, httpsUrl, final ProbeThread httpsProbe = new ProbeThread(latch, deps, proxy, httpsUrl, ValidationProbeEvent.PROBE_HTTPS, capportApiUrl); final ProbeThread httpProbe = new ProbeThread(latch, proxy, httpUrl, final ProbeThread httpProbe = new ProbeThread(latch, deps, proxy, httpUrl, ValidationProbeEvent.PROBE_HTTP, capportApiUrl); try { Loading Loading
apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java +5 −0 Original line number Diff line number Diff line Loading @@ -37,4 +37,9 @@ public class ConstantsShim extends com.android.networkstack.apishim.api29.Consta DataStallReport.DETECTION_METHOD_DNS_EVENTS; public static final int DETECTION_METHOD_TCP_METRICS = DataStallReport.DETECTION_METHOD_TCP_METRICS; /** * @see android.net.NetworkCapabilities */ public static final int TRANSPORT_TEST = 7; }
apishim/31/com/android/networkstack/apishim/ConstantsShim.java +4 −0 Original line number Diff line number Diff line Loading @@ -30,4 +30,8 @@ public class ConstantsShim extends com.android.networkstack.apishim.api30.Consta */ @VisibleForTesting public static final int VERSION = 31; // When removing this shim, the version in NetworkMonitorUtils should be removed too. // TODO: add TRANSPORT_TEST to system API in API 31 (it is only a test API as of R) public static final int TRANSPORT_TEST = 7; }
common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java +35 −3 Original line number Diff line number Diff line Loading @@ -20,11 +20,21 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import android.net.NetworkCapabilities; /** @hide */ public class NetworkMonitorUtils { // This class is used by both NetworkMonitor and ConnectivityService, so it cannot use // NetworkStack shims, but at the same time cannot use non-system APIs. // TRANSPORT_TEST is test API as of R (so it is enforced to always be 7 and can't be changed), // and it is being added as a system API in S. // TODO: use NetworkCapabilities.TRANSPORT_TEST once NetworkStack builds against API 31. private static final int TRANSPORT_TEST = 7; // Network conditions broadcast constants public static final String ACTION_NETWORK_CONDITIONS_MEASURED = Loading @@ -47,11 +57,33 @@ public class NetworkMonitorUtils { * @param nc Network capabilities of the network to test. */ public static boolean isPrivateDnsValidationRequired(NetworkCapabilities nc) { if (nc == null) return false; // TODO: Consider requiring validation for DUN networks. return nc != null && nc.hasCapability(NET_CAPABILITY_INTERNET) if (nc.hasCapability(NET_CAPABILITY_INTERNET) && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) && nc.hasCapability(NET_CAPABILITY_TRUSTED); && nc.hasCapability(NET_CAPABILITY_TRUSTED)) { // Real networks return true; } // TODO: once TRANSPORT_TEST is @SystemApi in S and S SDK is stable (so constant shims can // be replaced with the SDK constant that will be inlined), replace isTestNetwork with // hasTransport(TRANSPORT_TEST) // Test networks that also have one of the major transport types are attempting to replicate // that transport on a test interface (for example, test ethernet networks with // EthernetManager#setIncludeTestInterfaces). Run validation on them for realistic tests. // See also comments on EthernetManager#setIncludeTestInterfaces and on TestNetworkManager. if (nc.hasTransport(TRANSPORT_TEST) && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) && ( nc.hasTransport(TRANSPORT_WIFI) || nc.hasTransport(TRANSPORT_CELLULAR) || nc.hasTransport(TRANSPORT_BLUETOOTH) || nc.hasTransport(TRANSPORT_ETHERNET))) { return true; } return false; } /** Loading
src/android/net/ip/IpClient.java +17 −3 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; import android.util.Patterns; import android.util.SparseArray; import androidx.annotation.NonNull; Loading @@ -86,6 +85,8 @@ import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; Loading Loading @@ -1263,9 +1264,9 @@ public class IpClient extends StateMachine { } final String capportUrl = mDhcpResults.captivePortalApiUrl; // Uri.parse does no syntax check; do a simple regex check to eliminate garbage. // Uri.parse does no syntax check; do a simple check to eliminate garbage. // If the URL is still incorrect data fetching will fail later, which is fine. if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) { if (isParseableUrl(capportUrl)) { NetworkInformationShimImpl.newInstance() .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl)); } Loading Loading @@ -1303,6 +1304,19 @@ public class IpClient extends StateMachine { return newLp; } private static boolean isParseableUrl(String url) { // Verify that a URL has a reasonable format that can be parsed as per the URL constructor. // This does not use Patterns.WEB_URL as that pattern excludes URLs without TLDs, such as on // localhost. if (url == null) return false; try { new URL(url); return true; } catch (MalformedURLException e) { return false; } } private static void addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses) { // TODO: Investigate deleting this reachability check. We should be Loading
src/com/android/server/connectivity/NetworkMonitor.java +55 −26 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_DNS_EVENTS; import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_TCP_METRICS; import static com.android.networkstack.apishim.ConstantsShim.TRANSPORT_TEST; import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX; import static com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG; Loading Loading @@ -387,7 +388,8 @@ public class NetworkMonitor extends StateMachine { private static final int CMD_BANDWIDTH_CHECK_TIMEOUT = 24; // Start mReevaluateDelayMs at this value and double. private static final int INITIAL_REEVALUATE_DELAY_MS = 1000; @VisibleForTesting static final int INITIAL_REEVALUATE_DELAY_MS = 1000; private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000; // Default timeout of evaluating network bandwidth. private static final int DEFAULT_EVALUATING_BANDWIDTH_TIMEOUT_MS = 10_000; Loading Loading @@ -1090,7 +1092,7 @@ public class NetworkMonitor extends StateMachine { final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; // Use redirect URL from AP if exists. final String portalUrl = (useRedirectUrlForPortal() && probeRes.redirectUrl != null) (useRedirectUrlForPortal() && makeURL(probeRes.redirectUrl) != null) ? probeRes.redirectUrl : probeRes.detectUrl; appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, portalUrl); if (probeRes.probeSpec != null) { Loading Loading @@ -1434,8 +1436,9 @@ public class NetworkMonitor extends StateMachine { } final int token = ++mProbeToken; final EvaluationThreadDeps deps = new EvaluationThreadDeps(mNetworkCapabilities); mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0, isCaptivePortal()))); isCaptivePortal(deps)))); mThread.start(); } Loading Loading @@ -2147,8 +2150,25 @@ public class NetworkMonitor extends StateMachine { return mCaptivePortalFallbackSpecs[idx]; } @VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { /** * Parameters that can be accessed by the evaluation thread in a thread-safe way. * * Parameters such as LinkProperties and NetworkCapabilities cannot be accessed by the * evaluation thread directly, as they are managed in the state machine thread and not * synchronized. This class provides a copy of the required data that is not modified and can be * used safely by the evaluation thread. */ private static class EvaluationThreadDeps { // TODO: add parameters that are accessed in a non-thread-safe way from the evaluation // thread (read from LinkProperties, NetworkCapabilities, useHttps, validationStage) private final boolean mIsTestNetwork; EvaluationThreadDeps(NetworkCapabilities nc) { this.mIsTestNetwork = nc.hasTransport(TRANSPORT_TEST); } } private CaptivePortalProbeResult isCaptivePortal(EvaluationThreadDeps deps) { if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return CaptivePortalProbeResult.success(CaptivePortalProbeResult.PROBE_UNKNOWN); Loading Loading @@ -2196,11 +2216,11 @@ public class NetworkMonitor extends StateMachine { reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); } else if (mUseHttps && httpsUrls.length == 1 && httpUrls.length == 1) { // Probe results are reported inside sendHttpAndHttpsParallelWithFallbackProbes. result = sendHttpAndHttpsParallelWithFallbackProbes( proxyInfo, httpsUrls[0], httpUrls[0]); result = sendHttpAndHttpsParallelWithFallbackProbes(deps, proxyInfo, httpsUrls[0], httpUrls[0]); } else if (mUseHttps) { // Support result aggregation from multiple Urls. result = sendMultiParallelHttpAndHttpsProbes(proxyInfo, httpsUrls, httpUrls); result = sendMultiParallelHttpAndHttpsProbes(deps, proxyInfo, httpsUrls, httpUrls); } else { result = sendDnsAndHttpProbes(proxyInfo, httpUrls[0], ValidationProbeEvent.PROBE_HTTP); reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); Loading Loading @@ -2482,12 +2502,12 @@ public class NetworkMonitor extends StateMachine { private final CountDownLatch mLatch; private final Probe mProbe; ProbeThread(CountDownLatch latch, ProxyInfo proxy, URL url, int probeType, Uri captivePortalApiUrl) { ProbeThread(CountDownLatch latch, EvaluationThreadDeps deps, ProxyInfo proxy, URL url, int probeType, Uri captivePortalApiUrl) { mLatch = latch; mProbe = (probeType == ValidationProbeEvent.PROBE_HTTPS) ? new HttpsProbe(proxy, url, captivePortalApiUrl) : new HttpProbe(proxy, url, captivePortalApiUrl); ? new HttpsProbe(deps, proxy, url, captivePortalApiUrl) : new HttpProbe(deps, proxy, url, captivePortalApiUrl); mResult = CaptivePortalProbeResult.failed(probeType); } Loading @@ -2512,11 +2532,14 @@ public class NetworkMonitor extends StateMachine { } private abstract static class Probe { protected final EvaluationThreadDeps mDeps; protected final ProxyInfo mProxy; protected final URL mUrl; protected final Uri mCaptivePortalApiUrl; protected Probe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { protected Probe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { mDeps = deps; mProxy = proxy; mUrl = url; mCaptivePortalApiUrl = captivePortalApiUrl; Loading @@ -2526,8 +2549,8 @@ public class NetworkMonitor extends StateMachine { } final class HttpsProbe extends Probe { HttpsProbe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(proxy, url, captivePortalApiUrl); HttpsProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(deps, proxy, url, captivePortalApiUrl); } @Override Loading @@ -2537,8 +2560,8 @@ public class NetworkMonitor extends StateMachine { } final class HttpProbe extends Probe { HttpProbe(ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(proxy, url, captivePortalApiUrl); HttpProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) { super(deps, proxy, url, captivePortalApiUrl); } private CaptivePortalDataShim tryCapportApiProbe() { Loading @@ -2548,7 +2571,12 @@ public class NetworkMonitor extends StateMachine { final String apiContent; try { final URL url = new URL(mCaptivePortalApiUrl.toString()); if (!"https".equals(url.getProtocol())) { // Protocol must be HTTPS // (as per https://www.ietf.org/id/draft-ietf-capport-api-07.txt, #4). // Only allow HTTP on localhost, for testing. final boolean isTestLocalhostHttp = mDeps.mIsTestNetwork && "localhost".equals(url.getHost()) && "http".equals(url.getProtocol()); if (!"https".equals(url.getProtocol()) && !isTestLocalhostHttp) { validationLog("Invalid captive portal API protocol: " + url.getProtocol()); return null; } Loading Loading @@ -2631,8 +2659,9 @@ public class NetworkMonitor extends StateMachine { && captivePortalApiUrl == null); } private CaptivePortalProbeResult sendMultiParallelHttpAndHttpsProbes(@NonNull ProxyInfo proxy, @NonNull URL[] httpsUrls, @NonNull URL[] httpUrls) { private CaptivePortalProbeResult sendMultiParallelHttpAndHttpsProbes( @NonNull EvaluationThreadDeps deps, @Nullable ProxyInfo proxy, @NonNull URL[] httpsUrls, @NonNull URL[] httpUrls) { // If multiple URLs are required to ensure the correctness of validation, send parallel // probes to explore the result in separate probe threads and aggregate those results into // one as the final result for either HTTP or HTTPS. Loading @@ -2657,13 +2686,13 @@ public class NetworkMonitor extends StateMachine { // TODO: Have the capport probe as a different probe for cleanliness. final URL urlMaybeWithCapport = httpUrls[0]; for (final URL url : httpUrls) { futures.add(ecs.submit(() -> new HttpProbe(proxy, url, futures.add(ecs.submit(() -> new HttpProbe(deps, proxy, url, url.equals(urlMaybeWithCapport) ? capportApiUrl : null).sendProbe())); } for (final URL url : httpsUrls) { futures.add( ecs.submit(() -> new HttpsProbe(proxy, url, capportApiUrl).sendProbe())); futures.add(ecs.submit(() -> new HttpsProbe(deps, proxy, url, capportApiUrl) .sendProbe())); } final ArrayList<CaptivePortalProbeResult> completedProbes = new ArrayList<>(); Loading Loading @@ -2759,15 +2788,15 @@ public class NetworkMonitor extends StateMachine { } private CaptivePortalProbeResult sendHttpAndHttpsParallelWithFallbackProbes( ProxyInfo proxy, URL httpsUrl, URL httpUrl) { EvaluationThreadDeps deps, ProxyInfo proxy, URL httpsUrl, URL httpUrl) { // Number of probes to wait for. If a probe completes with a conclusive answer // it shortcuts the latch immediately by forcing the count to 0. final CountDownLatch latch = new CountDownLatch(2); final Uri capportApiUrl = getCaptivePortalApiUrl(mLinkProperties); final ProbeThread httpsProbe = new ProbeThread(latch, proxy, httpsUrl, final ProbeThread httpsProbe = new ProbeThread(latch, deps, proxy, httpsUrl, ValidationProbeEvent.PROBE_HTTPS, capportApiUrl); final ProbeThread httpProbe = new ProbeThread(latch, proxy, httpUrl, final ProbeThread httpProbe = new ProbeThread(latch, deps, proxy, httpUrl, ValidationProbeEvent.PROBE_HTTP, capportApiUrl); try { Loading