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

Commit 23243731 authored by Paul Jensen's avatar Paul Jensen
Browse files

If Internet probe is redirected, pass redirect destination to NetworkAgent.

Transport may use the redirect to make additional determination about network
state.

Bug: 25203607
Change-Id: I07d8918f13fdcbe0b6fd757536bfc1850a2a244f
parent 68b19f27
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -120,12 +120,17 @@ public abstract class NetworkAgent extends Handler {
     * either a bad network configuration (no internet link) or captive portal.
     *
     * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
     * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
     *       representing URL that Internet probe was redirect to, if it was redirected,
     *       or mapping to {@code null} otherwise.
     */
    public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;

    public static final int VALID_NETWORK = 1;
    public static final int INVALID_NETWORK = 2;

    public static String REDIRECT_URL_KEY = "redirect URL";

     /**
     * Sent by the NetworkAgent to ConnectivityService to indicate this network was
     * explicitly selected.  This should be sent before the NetworkInfo is marked
@@ -283,11 +288,12 @@ public abstract class NetworkAgent extends Handler {
                break;
            }
            case CMD_REPORT_NETWORK_STATUS: {
                String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
                if (VDBG) {
                    log("CMD_REPORT_NETWORK_STATUS(" +
                            (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
                            (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
                }
                networkStatus(msg.arg1);
                networkStatus(msg.arg1, redirectUrl);
                break;
            }
            case CMD_SAVE_ACCEPT_UNVALIDATED: {
@@ -443,8 +449,12 @@ public abstract class NetworkAgent extends Handler {
     *
     * This may be called multiple times as the network status changes and may
     * generate false negatives if we lose ip connectivity before the link is torn down.
     *
     * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
     * @param redirectUrl If the Internet probe was redirected, this is the destination it was
     *         redirected to, otherwise {@code null}.
     */
    protected void networkStatus(int status) {
    protected void networkStatus(int status, String redirectUrl) {
    }

    /**
+11 −5
Original line number Diff line number Diff line
@@ -2025,11 +2025,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
                default:
                    return false;
                case NetworkMonitor.EVENT_NETWORK_TESTED: {
                    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
                    if (isLiveNetworkAgent(nai, msg.what)) {
                    final NetworkAgentInfo nai;
                    synchronized (mNetworkForNetId) {
                        nai = mNetworkForNetId.get(msg.arg2);
                    }
                    if (nai != null) {
                        final boolean valid =
                                (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                        if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed"));
                        if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
                                (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
                        if (valid != nai.lastValidated) {
                            final int oldScore = nai.getCurrentScore();
                            nai.lastValidated = valid;
@@ -2040,10 +2044,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
                        }
                        updateInetCondition(nai);
                        // Let the NetworkAgent know the state of its network
                        Bundle redirectUrlBundle = new Bundle();
                        redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
                        nai.asyncChannel.sendMessage(
                                android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                                NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                                (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
                                0, null);
                                0, redirectUrlBundle);
                    }
                    break;
                }
+35 −15
Original line number Diff line number Diff line
@@ -120,8 +120,9 @@ public class NetworkMonitor extends StateMachine {

    /**
     * Inform ConnectivityService that the network has been tested.
     * obj = NetworkAgentInfo
     * obj = String representing URL that Internet probe was redirect to, if it was redirected.
     * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
     * arg2 = NetID.
     */
    public static final int EVENT_NETWORK_TESTED = BASE + 2;

@@ -334,8 +335,8 @@ public class NetworkMonitor extends StateMachine {
                            mDontDisplaySigninNotification = true;
                            mUserDoesNotWant = true;
                            mConnectivityServiceHandler.sendMessage(obtainMessage(
                                    EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
                                    mNetworkAgentInfo));
                                    EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
                                    mNetworkAgentInfo.network.netId, null));
                            // TODO: Should teardown network.
                            mUidResponsibleForReeval = 0;
                            transitionTo(mEvaluatingState);
@@ -358,7 +359,7 @@ public class NetworkMonitor extends StateMachine {
            CaptivePortalStateChangeEvent.logEvent(
                   CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
            mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                    NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
                    NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
        }

        @Override
@@ -412,6 +413,21 @@ public class NetworkMonitor extends StateMachine {
        }
    }

    /**
     * Result of calling isCaptivePortal().
     * @hide
     */
    @VisibleForTesting
    public static final class CaptivePortalProbeResult {
        final int mHttpResponseCode; // HTTP response code returned from Internet probe.
        final String mRedirectUrl;   // Redirect destination returned from Internet probe.

        public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl) {
            mHttpResponseCode = httpResponseCode;
            mRedirectUrl = redirectUrl;
        }
    }

    // Being in the EvaluatingState State indicates the Network is being evaluated for internet
    // connectivity, or that the user has indicated that this network is unwanted.
    private class EvaluatingState extends State {
@@ -464,19 +480,23 @@ public class NetworkMonitor extends StateMachine {
                    // IPv6) could each take SOCKET_TIMEOUT_MS.  During this time this StateMachine
                    // will be unresponsive. isCaptivePortal() could be executed on another Thread
                    // if this is found to cause problems.
                    int httpResponseCode = isCaptivePortal();
                    CaptivePortalProbeResult probeResult = isCaptivePortal();
                    CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
                            httpResponseCode);
                    if (httpResponseCode == 204) {
                            probeResult.mHttpResponseCode);
                    if (probeResult.mHttpResponseCode == 204) {
                        transitionTo(mValidatedState);
                    } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
                    } else if (probeResult.mHttpResponseCode >= 200 &&
                            probeResult.mHttpResponseCode <= 399) {
                        mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                                NETWORK_TEST_RESULT_INVALID, mNetworkAgentInfo.network.netId,
                                probeResult.mRedirectUrl));
                        transitionTo(mCaptivePortalState);
                    } else {
                        final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
                        sendMessageDelayed(msg, mReevaluateDelayMs);
                        mConnectivityServiceHandler.sendMessage(obtainMessage(
                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
                                mNetworkAgentInfo));
                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
                                mNetworkAgentInfo.network.netId, probeResult.mRedirectUrl));
                        if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
                            // Don't continue to blame UID forever.
                            TrafficStats.clearThreadStatsUid();
@@ -533,8 +553,6 @@ public class NetworkMonitor extends StateMachine {

        @Override
        public void enter() {
            mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                    NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
            // Don't annoy user with sign-in notifications.
            if (mDontDisplaySigninNotification) return;
            // Create a CustomIntentReceiver that sends us a
@@ -639,11 +657,12 @@ public class NetworkMonitor extends StateMachine {
     * Returns HTTP response code.
     */
    @VisibleForTesting
    protected int isCaptivePortal() {
        if (!mIsCaptivePortalCheckEnabled) return 204;
    protected CaptivePortalProbeResult isCaptivePortal() {
        if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204, null);

        HttpURLConnection urlConnection = null;
        int httpResponseCode = 599;
        String redirectUrl = null;
        try {
            URL url = new URL(getCaptivePortalServerUrl(mContext));
            // On networks with a PAC instead of fetching a URL that should result in a 204
@@ -699,6 +718,7 @@ public class NetworkMonitor extends StateMachine {
            long requestTimestamp = SystemClock.elapsedRealtime();

            httpResponseCode = urlConnection.getResponseCode();
            redirectUrl = urlConnection.getHeaderField("location");

            // Time how long it takes to get a response to our request
            long responseTimestamp = SystemClock.elapsedRealtime();
@@ -739,7 +759,7 @@ public class NetworkMonitor extends StateMachine {
                urlConnection.disconnect();
            }
        }
        return httpResponseCode;
        return new CaptivePortalProbeResult(httpResponseCode, redirectUrl);
    }

    /**
+28 −5
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ import android.util.LogPrinter;
import com.android.internal.util.WakeupMessage;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
import com.android.server.net.NetworkPinner;

import java.net.InetAddress;
@@ -223,11 +224,15 @@ public class ConnectivityServiceTest extends AndroidTestCase {
        private final NetworkCapabilities mNetworkCapabilities;
        private final IdleableHandlerThread mHandlerThread;
        private final ConditionVariable mDisconnected = new ConditionVariable();
        private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
        private int mScore;
        private NetworkAgent mNetworkAgent;
        private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
        private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
        private Integer mExpectedKeepaliveSlot = null;
        // Contains the redirectUrl from networkStatus(). Before reading, wait for
        // mNetworkStatusReceived.
        private String mRedirectUrl;

        MockNetworkAgent(int transport) {
            final int type = transportToLegacyType(transport);
@@ -266,6 +271,12 @@ public class ConnectivityServiceTest extends AndroidTestCase {
                public void stopPacketKeepalive(Message msg) {
                    onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
                }

                @Override
                public void networkStatus(int status, String redirectUrl) {
                    mRedirectUrl = redirectUrl;
                    mNetworkStatusReceived.open();
                }
            };
            // Waits for the NetworkAgent to be registered, which includes the creation of the
            // NetworkMonitor.
@@ -340,13 +351,15 @@ public class ConnectivityServiceTest extends AndroidTestCase {
            if (callback != null) mCm.unregisterNetworkCallback(callback);
        }

        public void connectWithCaptivePortal() {
        public void connectWithCaptivePortal(String redirectUrl) {
            mWrappedNetworkMonitor.gen204ProbeResult = 200;
            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
            connect(false);
            waitFor(new Criteria() { public boolean get() {
                NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
                return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
            mWrappedNetworkMonitor.gen204ProbeResult = 500;
            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null;
        }

        public void disconnect() {
@@ -381,6 +394,11 @@ public class ConnectivityServiceTest extends AndroidTestCase {
        public void setExpectedKeepaliveSlot(Integer slot) {
            mExpectedKeepaliveSlot = slot;
        }

        public String waitForRedirectUrl() {
            assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
            return mRedirectUrl;
        }
    }

    /**
@@ -543,6 +561,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
    private class WrappedNetworkMonitor extends NetworkMonitor {
        // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
        public int gen204ProbeResult = 500;
        public String gen204ProbeRedirectUrl = null;

        public WrappedNetworkMonitor(Context context, Handler handler,
            NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) {
@@ -550,8 +569,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
        }

        @Override
        protected int isCaptivePortal() {
            return gen204ProbeResult;
        protected CaptivePortalProbeResult isCaptivePortal() {
            return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
        }

        @Override
@@ -1344,8 +1363,10 @@ public class ConnectivityServiceTest extends AndroidTestCase {
        // Bring up a network with a captive portal.
        // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
        mWiFiNetworkAgent.connectWithCaptivePortal();
        String firstRedirectUrl = "http://example.com/firstPath";
        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
        captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
        assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);

        // Take down network.
        // Expect onLost callback.
@@ -1355,8 +1376,10 @@ public class ConnectivityServiceTest extends AndroidTestCase {
        // Bring up a network with a captive portal.
        // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
        mWiFiNetworkAgent.connectWithCaptivePortal();
        String secondRedirectUrl = "http://example.com/secondPath";
        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
        captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
        assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);

        // Make captive portal disappear then revalidate.
        // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.