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

Commit 4e838fa5 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 7668448 from 0628bd22 to mainline-wifi-release

Change-Id: Ie2822ad1dde552e3e70275e956660882309cd380
parents 951776bc 0628bd22
Loading
Loading
Loading
Loading
+35 −14
Original line number Diff line number Diff line
@@ -156,6 +156,8 @@ public class IpReachabilityMonitor {
    protected static final int NUD_MCAST_RESOLICIT_NUM = 3;
    private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1;

    private static final int INVALID_LEGACY_NUD_FAILURE_TYPE = -1;

    public interface Callback {
        /**
         * This callback function must execute as quickly as possible as it is
@@ -408,6 +410,7 @@ public class IpReachabilityMonitor {
                    + " to: " + event.macAddr;
            mLog.w(logMsg);
            mCallback.notifyLost(event.ip, logMsg);
            logNudFailed(event, NudEventType.NUD_MAC_ADDRESS_CHANGED);
            return;
        }
        maybeRestoreNeighborParameters();
@@ -446,6 +449,8 @@ public class IpReachabilityMonitor {
        final boolean lostProvisioning =
                (mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned())
                || (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned());
        final NudEventType type = getNudFailureEventType(isFromProbe(),
                isNudFailureDueToRoam(), lostProvisioning);

        if (lostProvisioning) {
            final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
@@ -454,7 +459,7 @@ public class IpReachabilityMonitor {
            // an InetAddress argument.
            mCallback.notifyLost(ip, logMsg);
        }
        logNudFailed(event, lostProvisioning);
        logNudFailed(event, type);
    }

    private void maybeRestoreNeighborParameters() {
@@ -574,14 +579,16 @@ public class IpReachabilityMonitor {
        return duration < getProbeWakeLockDuration();
    }

    private boolean isProbedNudFailureDueToRoam() {
    private boolean isNudFailureDueToRoam() {
        if (!isFromProbe()) return false;

        // Check to which probe expiry the curren timestamp gets close when NUD failure event
        // happens, theoretically that indicates which probe event(due to roam or CMD_CONFIRM)
        // was triggered eariler.
        //
        // Note that this would be incorrect if the probe or confirm was so long ago that the
        // probe duration has already expired. That cannot happen because isFromProbe would return
        // false, and this method is only called on NUD failures due to probes.
        // false.
        final long probeExpiryAfterRoam = mLastProbeDueToRoamMs + getProbeWakeLockDuration();
        final long probeExpiryAfterConfirm =
                mLastProbeDueToConfirmMs + getProbeWakeLockDuration();
@@ -595,10 +602,15 @@ public class IpReachabilityMonitor {
        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
    }

    private void logNudFailed(final NeighborEvent event, boolean lostProvisioning) {
        final int eventType = nudFailureEventType(isFromProbe(), lostProvisioning);
    private void logNudFailed(final NeighborEvent event, final NudEventType type) {
        logNeighborLostEvent(event, type);

        // The legacy metrics only record whether the failure came from a probe and whether
        // the network is still provisioned. They do not record provisioning failures due to
        // multicast resolicits finding that the MAC address has changed.
        final int eventType = legacyNudFailureType(type);
        if (eventType == INVALID_LEGACY_NUD_FAILURE_TYPE) return;
        mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
        logNeighborLostEvent(event, lostProvisioning);
    }

    /**
@@ -637,23 +649,32 @@ public class IpReachabilityMonitor {
     * Log NUD failure metrics with new Westworld APIs while the function using mMetricsLog API
     * still sends the legacy metrics, @see #logNudFailed.
     */
    private void logNeighborLostEvent(final NeighborEvent event, boolean isProvisioningLost) {
    private void logNeighborLostEvent(final NeighborEvent event, final NudEventType type) {
        final IpType ipType = (event.ip instanceof Inet6Address) ? IpType.IPV6 : IpType.IPV4;
        mIpReachabilityMetrics.setNudIpType(ipType);
        mIpReachabilityMetrics.setNudNeighborType(getNeighborType(event));
        mIpReachabilityMetrics.setNudEventType(getNudFailureEventType(isFromProbe(),
                isProbedNudFailureDueToRoam(), isProvisioningLost));
        mIpReachabilityMetrics.setNudEventType(type);
        mIpReachabilityMetrics.statsWrite();
    }

    /**
     * Returns the NUD failure event type code corresponding to the given conditions.
     */
    private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
        if (isFromProbe) {
            return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
        } else {
            return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
    private static int legacyNudFailureType(final NudEventType type) {
        switch (type) {
            case NUD_POST_ROAMING_FAILED:
            case NUD_CONFIRM_FAILED:
                return NUD_FAILED;
            case NUD_POST_ROAMING_FAILED_CRITICAL:
            case NUD_CONFIRM_FAILED_CRITICAL:
                return PROVISIONING_LOST;
            case NUD_ORGANIC_FAILED:
                return NUD_FAILED_ORGANIC;
            case NUD_ORGANIC_FAILED_CRITICAL:
                return PROVISIONING_LOST_ORGANIC;
            default:
                // Do not log legacy event
                return INVALID_LEGACY_NUD_FAILURE_TYPE;
        }
    }
}
+54 −8
Original line number Diff line number Diff line
@@ -388,7 +388,7 @@ public class NetworkMonitor extends StateMachine {
    private static final int CMD_BANDWIDTH_CHECK_COMPLETE = 23;

    /**
     * Message to self to know the bandwidth check is timeouted.
     * Message to self to know the bandwidth check has timed out.
     */
    private static final int CMD_BANDWIDTH_CHECK_TIMEOUT = 24;

@@ -636,6 +636,13 @@ public class NetworkMonitor extends StateMachine {

    /**
     * Request the NetworkMonitor to reevaluate the network.
     *
     * TODO : refactor reevaluation to introduce rate limiting. If the system finds a network is
     * validated but some app can't access their server, or the network is behind a captive portal
     * that only lets the validation URL through, apps may be calling reportNetworkConnectivity
     * often, causing many revalidation attempts. Meanwhile, reevaluation attempts that result
     * from actions that may affect the validation status (e.g. the user just logged in through
     * the captive portal app) should never be skipped because of the rate limitation.
     */
    public void forceReevaluation(int responsibleUid) {
        sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
@@ -916,6 +923,18 @@ public class NetworkMonitor extends StateMachine {
                            // If the user wants to use this network anyway, there is no need to
                            // perform the bandwidth check even if configured.
                            mIsBandwidthCheckPassedOrIgnored = true;
                            // If the user wants to use this network anyway, it should always
                            // be reported as validated, but other checks still need to be
                            // done. For example, it should still validate strict private DNS and
                            // show a notification if not available, because the network will
                            // be unusable for this additional reason.
                            mEvaluationState.setCaptivePortalWantedAsIs();
                            // A successful evaluation result should be reported immediately, so
                            // that the network stack may immediately use the validation in ranking
                            // without waiting for a possibly long private DNS or bandwidth eval
                            // step.
                            mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_VALID,
                                    null);
                            // TODO: Distinguish this from a network that actually validates.
                            // Displaying the "x" on the system UI icon may still be a good idea.
                            transitionTo(mEvaluatingPrivateDnsState);
@@ -1290,11 +1309,19 @@ public class NetworkMonitor extends StateMachine {
                    //    the network so don't bother validating here.  Furthermore sending HTTP
                    //    packets over the network may be undesirable, for example an extremely
                    //    expensive metered network, or unwanted leaking of the User Agent string.
                    // Also don't bother validating networks that the user already said they
                    // wanted as-is.
                    //
                    // On networks that need to support private DNS in strict mode (e.g., VPNs, but
                    // not networks that don't provide Internet access), we still need to perform
                    // private DNS server resolution.
                    if (!isValidationRequired()) {
                    if (mEvaluationState.isCaptivePortalWantedAsIs()
                            && isPrivateDnsValidationRequired()) {
                        // Captive portals can only be detected on networks that validate both
                        // validation and private DNS validation.
                        validationLog("Captive portal is used as is, resolving private DNS");
                        transitionTo(mEvaluatingPrivateDnsState);
                    } else if (!isValidationRequired()) {
                        if (isPrivateDnsValidationRequired()) {
                            validationLog("Network would not satisfy default request, "
                                    + "resolving private DNS");
@@ -3390,18 +3417,28 @@ public class NetworkMonitor extends StateMachine {
    // NETWORK_VALIDATION_RESULT_VALID. But with this scheme, the first two or three validation
    // reports are all failures, because they are "HTTP succeeded but validation not yet passed",
    // "HTTP and HTTPS succeeded but validation not yet passed", etc.
    // TODO : rename EvaluationState to not contain "State" in the name, as it makes this class
    // sound like one of the states of the state machine, which it's not.
    @VisibleForTesting
    protected class EvaluationState {
        // The latest validation result for this network. This is a bitmask of
        // INetworkMonitor.NETWORK_VALIDATION_RESULT_* constants.
        private int mEvaluationResult = NETWORK_VALIDATION_RESULT_INVALID;


        // Set when the captive portal app said this network should be used as is as a result
        // of user interaction. The valid bit represents the user's decision to override automatic
        // determination of whether the network has access to Internet, so in this case the
        // network is always reported as validated.
        // TODO : Make ConnectivityService aware of this state, so that it can use the network as
        // the default without setting the VALIDATED bit, as it's a bit of a lie. This can't be
        // done on Android <= R where CS can't be updated, but it is doable on S+.
        private boolean mCaptivePortalWantedAsIs = false;
        // Indicates which probes have succeeded since clearProbeResults was called.
        // This is a bitmask of INetworkMonitor.NETWORK_VALIDATION_PROBE_* constants.
        private int mProbeResults = 0;
        // A bitmask to record which probes are completed.
        private int mProbeCompleted = 0;
        // The latest redirect URL.
        private String mRedirectUrl;

        protected void clearProbeResults() {
            mProbeResults = 0;
@@ -3435,17 +3472,26 @@ public class NetworkMonitor extends StateMachine {
            });
        }

        protected void setCaptivePortalWantedAsIs() {
            mCaptivePortalWantedAsIs = true;
        }

        protected boolean isCaptivePortalWantedAsIs() {
            return mCaptivePortalWantedAsIs;
        }

        protected void reportEvaluationResult(int result, @Nullable String redirectUrl) {
            if (!isValidationRequired() && mProbeCompleted == 0 && ShimUtils.isAtLeastS()) {
            if (mCaptivePortalWantedAsIs) {
                result = NETWORK_VALIDATION_RESULT_VALID;
            } else if (!isValidationRequired() && mProbeCompleted == 0 && mCallbackVersion >= 11) {
                // If validation is not required AND no probes were attempted, the validation was
                // skipped. Report this to ConnectivityService for ConnectivityDiagnostics, but only
                // if the platform is Android S+, as ConnectivityService must also know how to
                // understand this bit.
                // if the platform has callback version 11+, as ConnectivityService must also know
                // how to understand this bit.
                result |= NETWORK_VALIDATION_RESULT_SKIPPED;
            }

            mEvaluationResult = result;
            mRedirectUrl = redirectUrl;
            final NetworkTestResultParcelable p = new NetworkTestResultParcelable();
            p.result = result;
            p.probesSucceeded = mProbeResults;
+38 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.net.LinkProperties
import android.net.RouteInfo
import android.net.metrics.IpConnectivityLog
import android.net.util.InterfaceParams
import android.net.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION
import android.net.util.SharedLog
import android.os.Handler
import android.os.HandlerThread
@@ -36,6 +37,7 @@ import android.stats.connectivity.IpType.IPV6
import android.stats.connectivity.NudEventType
import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED
import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED_CRITICAL
import android.stats.connectivity.NudEventType.NUD_MAC_ADDRESS_CHANGED
import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED
import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL
import android.stats.connectivity.NudEventType.NUD_ORGANIC_FAILED
@@ -50,6 +52,7 @@ import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.networkstack.metrics.IpReachabilityMonitorMetrics
import com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED
import com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE
import com.android.net.module.util.netlink.StructNdMsg.NUD_STALE
import com.android.testutils.makeNewNeighMessage
import com.android.testutils.waitForIdle
@@ -59,7 +62,9 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyObject
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.doAnswer
@@ -324,6 +329,29 @@ class IpReachabilityMonitorTest {
        verifyNudFailureMetrics(eventType, ipType, lostNeighborType)
    }

    private fun runNeighborReachableButMacAddrChangedTest(
        newLp: LinkProperties,
        neighbor: InetAddress,
        ipType: IpType
    ) {
        doReturn(true).`when`(dependencies).isFeatureEnabled(anyObject(),
                eq(IP_REACHABILITY_MCAST_RESOLICIT_VERSION), anyBoolean())

        reachabilityMonitor.updateLinkProperties(newLp)

        neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
                "001122334455" /* oldMac */))
        handlerThread.waitForIdle(TEST_TIMEOUT_MS)
        verify(callback, never()).notifyLost(eq(neighbor), anyString())

        reachabilityMonitor.probeAll(true /* dueToRoam */)

        neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
                "1122334455aa" /* newMac */))
        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString())
        verifyNudFailureMetrics(NUD_MAC_ADDRESS_CHANGED, ipType, NUD_NEIGHBOR_GATEWAY)
    }

    @Test
    fun testLoseProvisioning_Ipv4DnsLost() {
        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS)
@@ -519,4 +547,14 @@ class IpReachabilityMonitorTest {

        verifyNudFailureMetrics(NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY)
    }

    @Test
    fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChanged() {
        runNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, IPV6)
    }

    @Test
    fun testNudProbeFailedMetrics_defaultIPv4GatewayMacAddrChanged() {
        runNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, IPV4)
    }
}
+53 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.connectivity;

import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -588,7 +589,7 @@ public class NetworkMonitorTest {
    }

    private void resetCallbacks() {
        resetCallbacks(6);
        resetCallbacks(11);
    }

    private void resetCallbacks(int interfaceVersion) {
@@ -1795,12 +1796,8 @@ public class NetworkMonitorTest {
        runFailedNetworkTest();
    }

    private void doValidationSkippedTest(NetworkCapabilities nc) throws Exception {
        // For S+, the RESULT_SKIPPED bit will be included on networks that both do not require
        // validation and for which validation is not performed.
        final int validationResult = ShimUtils.isAtLeastS()
                ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED
                : NETWORK_VALIDATION_RESULT_VALID;
    private void doValidationSkippedTest(NetworkCapabilities nc, int validationResult)
            throws Exception {
        runNetworkTest(TEST_LINK_PROPERTIES, nc, validationResult,
                0 /* probesSucceeded */, null /* redirectUrl */);
        verify(mCleartextDnsNetwork, never()).openConnection(any());
@@ -1808,7 +1805,15 @@ public class NetworkMonitorTest {

    @Test
    public void testNoInternetCapabilityValidated() throws Exception {
        doValidationSkippedTest(CELL_NO_INTERNET_CAPABILITIES);
        doValidationSkippedTest(CELL_NO_INTERNET_CAPABILITIES,
                NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED);
    }

    @Test
    public void testNoInternetCapabilityValidated_OlderPlatform() throws Exception {
        // Before callbacks version 11, NETWORK_VALIDATION_RESULT_SKIPPED is not sent
        resetCallbacks(10);
        doValidationSkippedTest(CELL_NO_INTERNET_CAPABILITIES, NETWORK_VALIDATION_RESULT_VALID);
    }

    @Test
@@ -1821,7 +1826,8 @@ public class NetworkMonitorTest {
        if (ShimUtils.isAtLeastS()) {
            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
        }
        doValidationSkippedTest(nc);
        doValidationSkippedTest(nc,
                NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED);
    }

    @Test
@@ -1834,7 +1840,8 @@ public class NetworkMonitorTest {
        if (ShimUtils.isAtLeastS()) {
            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
        }
        doValidationSkippedTest(nc);
        doValidationSkippedTest(nc,
                NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED);
    }

    private NetworkCapabilities getVcnUnderlyingCarrierWifiCaps() {
@@ -1878,12 +1885,10 @@ public class NetworkMonitorTest {
                nm.getEvaluationState().getEvaluationResult());
    }

    @Test
    public void testLaunchCaptivePortalApp() throws Exception {
    public void setupAndLaunchCaptivePortalApp(final NetworkMonitor nm) throws Exception {
        setSslException(mHttpsConnection);
        setPortal302(mHttpConnection);
        when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(TEST_LOGIN_URL);
        final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
        notifyNetworkConnected(nm, CELL_METERED_CAPABILITIES);

        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
@@ -1910,11 +1915,18 @@ public class NetworkMonitorTest {
        final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
        assertEquals(TEST_HTTP_URL, redirectUrl);

        resetCallbacks();
    }

    @Test
    public void testCaptivePortalLogin() throws Exception {
        final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
        setupAndLaunchCaptivePortalApp(nm);

        // Have the app report that the captive portal is dismissed, and check that we revalidate.
        setStatus(mHttpsConnection, 204);
        setStatus(mHttpConnection, 204);

        resetCallbacks();
        nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
                .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable(
@@ -1923,6 +1935,31 @@ public class NetworkMonitorTest {
        assertEquals(0, mRegisteredReceivers.size());
    }

    @Test
    public void testCaptivePortalUseAsIs() throws Exception {
        final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
        setupAndLaunchCaptivePortalApp(nm);

        // The user decides this network is wanted as is, either by encountering an SSL error or
        // encountering an unknown scheme and then deciding to continue through the browser, or by
        // selecting this option through the options menu.
        nm.notifyCaptivePortalAppFinished(APP_RETURN_WANTED_AS_IS);
        // The captive portal is still closed, but the network validates since the user said so.
        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
                .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable(
                        NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */));
        resetCallbacks();

        // Revalidate.
        nm.forceReevaluation(0 /* responsibleUid */);

        // The network should still be valid.
        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
                .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable(
                        NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */,
                        TEST_LOGIN_URL));
    }

    @Test
    public void testPrivateDnsSuccess() throws Exception {
        setStatus(mHttpsConnection, 204);
@@ -2758,9 +2795,8 @@ public class NetworkMonitorTest {
                new NetworkCapabilities(WIFI_OEM_PAID_CAPABILITIES);
        networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);

        final int validationResult = ShimUtils.isAtLeastS()
                ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED
                : NETWORK_VALIDATION_RESULT_VALID;
        final int validationResult =
                NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED;
        runNetworkTest(TEST_LINK_PROPERTIES, networkCapabilities,
                validationResult, 0 /* probesSucceeded */, null /* redirectUrl */);