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

Commit b0573967 authored by lucaslin's avatar lucaslin Committed by Lucas Lin
Browse files

Check if network has partial connectivity

In some networks, network validation may only get success
result for http probe but fail result for https probe.
For this kind of network, it may still work at some websites
or apps, but user didn't know about that. In order to fix this
issue, we will check if network has partial connectivity and
notify user to make a choice if they want to use this partial
connectivity or not.

Bug: 113450764
Test: 1. Build pass.
      2. Fake partial connectivity case for testing.
      3. atest FrameworksNetTests
      4. atest NetworkStackTests

Change-Id: I69ed00ac4850904ff708c9fef22e148879a10e92
parent e9bdece8
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -252,6 +252,12 @@ public class NetworkStackService extends Service {
            mNm.notifyCaptivePortalAppFinished(response);
        }

        @Override
        public void notifyAcceptPartialConnectivity() {
            checkNetworkStackCallingPermission();
            mNm.notifyAcceptPartialConnectivity();
        }

        @Override
        public void forceReevaluation(int uid) {
            checkNetworkStackCallingPermission();
+36 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -227,6 +228,12 @@ public class NetworkMonitor extends StateMachine {
     */
    public static final int EVENT_DNS_NOTIFICATION = 17;

    /**
     * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
     * NetworkMonitor should ignore the https probe.
     */
    public static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18;

    // Start mReevaluateDelayMs at this value and double.
    private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
    private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
@@ -378,6 +385,14 @@ public class NetworkMonitor extends StateMachine {
        mNetworkCapabilities = new NetworkCapabilities(null);
    }

    /**
     * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
     * NetworkMonitor should ignore the https probe.
     */
    public void notifyAcceptPartialConnectivity() {
        sendMessage(EVENT_ACCEPT_PARTIAL_CONNECTIVITY);
    }

    /**
     * Request the NetworkMonitor to reevaluate the network.
     */
@@ -636,6 +651,10 @@ public class NetworkMonitor extends StateMachine {
                case EVENT_DNS_NOTIFICATION:
                    mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
                    break;
                case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
                    mUseHttps = false;
                    transitionTo(mEvaluatingPrivateDnsState);
                    break;
                default:
                    break;
            }
@@ -1058,6 +1077,11 @@ public class NetworkMonitor extends StateMachine {
                        notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
                        mLastPortalProbeResult = probeResult;
                        transitionTo(mCaptivePortalState);
                    } else if (probeResult.isPartialConnectivity()) {
                        logNetworkEvent(NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY);
                        notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY,
                                probeResult.redirectUrl);
                        transitionTo(mWaitingForNextProbeState);
                    } else {
                        logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
                        notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
@@ -1065,7 +1089,8 @@ public class NetworkMonitor extends StateMachine {
                    }
                    return HANDLED;
                case EVENT_DNS_NOTIFICATION:
                    // Leave the event to DefaultState to record correct dns timestamp.
                case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
                    // Leave the event to DefaultState.
                    return NOT_HANDLED;
                default:
                    // Wait for probe result and defer events to next state by default.
@@ -1504,10 +1529,11 @@ public class NetworkMonitor extends StateMachine {
        // 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();
        CaptivePortalProbeResult fallbackProbeResult = null;
        if (fallbackUrl != null) {
            CaptivePortalProbeResult result = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
            if (result.isPortal()) {
                return result;
            fallbackProbeResult = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
            if (fallbackProbeResult.isPortal()) {
                return fallbackProbeResult;
            }
        }
        // Otherwise wait until http and https probes completes and use their results.
@@ -1517,6 +1543,12 @@ public class NetworkMonitor extends StateMachine {
                return httpProbe.result();
            }
            httpsProbe.join();
            final boolean isHttpSuccessful =
                    (httpProbe.result().isSuccessful()
                    || (fallbackProbeResult != null && fallbackProbeResult.isSuccessful()));
            if (httpsProbe.result().isFailed() && isHttpSuccessful) {
                return CaptivePortalProbeResult.PARTIAL;
            }
            return httpsProbe.result();
        } catch (InterruptedException e) {
            validationLog("Error: http or https probe wait interrupted!");
+33 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.connectivity;

import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.provider.Settings.Global.DATA_STALL_EVALUATION_TYPE_DNS;
@@ -572,6 +573,34 @@ public class NetworkMonitorTest {
                stats.build());
    }

    @Test
    public void testIgnoreHttpsProbe() throws Exception {
        setSslException(mHttpsConnection);
        setStatus(mHttpConnection, 204);

        final NetworkMonitor nm = makeMonitor();
        nm.notifyNetworkConnected();
        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
                .notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY, null);

        nm.notifyAcceptPartialConnectivity();
        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
                .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
    }

    @Test
    public void testIsPartialConnectivity() throws IOException {
        setStatus(mHttpsConnection, 500);
        setStatus(mHttpConnection, 204);
        setStatus(mFallbackConnection, 500);
        assertPartialConnectivity(makeMonitor().isCaptivePortal());

        setStatus(mHttpsConnection, 500);
        setStatus(mHttpConnection, 500);
        setStatus(mFallbackConnection, 204);
        assertPartialConnectivity(makeMonitor().isCaptivePortal());
    }

    private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
        for (int i = 0; i < count; i++) {
            wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -649,6 +678,10 @@ public class NetworkMonitorTest {
        assertFalse(result.isSuccessful());
    }

    private void assertPartialConnectivity(CaptivePortalProbeResult result) {
        assertTrue(result.isPartialConnectivity());
    }

    private void setSslException(HttpURLConnection connection) throws IOException {
        doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
    }