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

Commit eada781b authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Xiao Ma
Browse files

Provide RRO to configure neighbor unreachable probes parameters.

Bug: 130871097
Test: atest FrameworksNetTests NetworkStackTests
Merged-In: Idc2c4303640d4fd3fc29dd0957771088820030ff
(cherry picked from commit f5402060)

Change-Id: Icd69008635461aa24d976f9b9d2d0ef1d60e23d5
parent 97397fad
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -49,4 +49,10 @@

    <!-- Configuration for including DHCP client hostname option -->
    <bool name="config_dhcp_client_hostname">false</bool>

    <!-- Customized neighbor unreachable probe parameters. -->
    <integer name="config_nud_steadystate_solicit_num">10</integer>
    <integer name="config_nud_steadystate_solicit_interval">750</integer>
    <integer name="config_nud_postroaming_solicit_num">5</integer>
    <integer name="config_nud_postroaming_solicit_interval">750</integer>
</resources>
+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,14 @@
            random number and etc.
            -->
            <item type="bool" name="config_dhcp_client_hostname"/>
            <!-- Customized neighbor unreachable probe parameters.
            Legal config_*_num value should be in the range of 5-15; and config_*_interval value
            should be in the range of 750-1000ms.
            -->
            <item type="integer" name="config_nud_steadystate_solicit_num"/>
            <item type="integer" name="config_nud_steadystate_solicit_interval"/>
            <item type="integer" name="config_nud_postroaming_solicit_num"/>
            <item type="integer" name="config_nud_postroaming_solicit_interval"/>
        </policy>
    </overlayable>
</resources>
+2 −30
Original line number Diff line number Diff line
@@ -1363,19 +1363,6 @@ public class IpClient extends StateMachine {
    }

    private boolean startIpReachabilityMonitor() {
        try {
            // TODO: Fetch these parameters from settings, and install a
            // settings observer to watch for update and re-program these
            // parameters (Q: is this level of dynamic updatability really
            // necessary or does reading from settings at startup suffice?).
            final int numSolicits = 5;
            final int interSolicitIntervalMs = 750;
            setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs);
        } catch (Exception e) {
            mLog.e("Failed to adjust neighbor parameters", e);
            // Carry on using the system defaults (currently: 3, 1000);
        }

        try {
            mIpReachabilityMonitor = new IpReachabilityMonitor(
                    mContext,
@@ -1388,7 +1375,8 @@ public class IpClient extends StateMachine {
                            mCallback.onReachabilityLost(logMsg);
                        }
                    },
                    mConfiguration.mUsingMultinetworkPolicyTracker);
                    mConfiguration.mUsingMultinetworkPolicyTracker,
                    mNetd);
        } catch (IllegalArgumentException iae) {
            // Failed to start IpReachabilityMonitor. Log it and call
            // onProvisioningFailure() immediately.
@@ -1986,22 +1974,6 @@ public class IpClient extends StateMachine {
        }
    }

    private static void setNeighborParameters(
            INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
            throws RemoteException, IllegalArgumentException {
        Preconditions.checkNotNull(netd);
        Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
        Preconditions.checkArgument(numSolicits > 0);
        Preconditions.checkArgument(interSolicitIntervalMs > 0);

        for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
            netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
                    Integer.toString(interSolicitIntervalMs));
            netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
                    Integer.toString(numSolicits));
        }
    }

    // TODO: extract out into CollectionUtils.
    static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
        for (T t : coll) {
+102 −22
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.LinkProperties;
import android.net.RouteInfo;
import android.net.ip.IpNeighborMonitor.NeighborEvent;
@@ -36,10 +37,14 @@ import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.networkstack.R;

import java.io.PrintWriter;
import java.net.Inet6Address;
@@ -132,12 +137,20 @@ public class IpReachabilityMonitor {
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);

    // Upper and lower bound for NUD probe parameters.
    protected static final int MAX_NUD_SOLICIT_NUM = 15;
    protected static final int MIN_NUD_SOLICIT_NUM = 5;
    protected static final int MAX_NUD_SOLICIT_INTERVAL_MS = 1000;
    protected static final int MIN_NUD_SOLICIT_INTERVAL_MS = 750;

    public interface Callback {
        // This callback function must execute as quickly as possible as it is
        // run on the same thread that listens to kernel neighbor updates.
        //
        // TODO: refactor to something like notifyProvisioningLost(String msg).
        public void notifyLost(InetAddress ip, String logMsg);
        /**
         * This callback function must execute as quickly as possible as it is
         * run on the same thread that listens to kernel neighbor updates.
         *
         * TODO: refactor to something like notifyProvisioningLost(String msg).
         */
        void notifyLost(InetAddress ip, String logMsg);
    }

    /**
@@ -168,29 +181,47 @@ public class IpReachabilityMonitor {
    private final boolean mUsingMultinetworkPolicyTracker;
    private final ConnectivityManager mCm;
    private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
    private final Context mContext;
    private final INetd mNetd;
    private LinkProperties mLinkProperties = new LinkProperties();
    private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
    // Time in milliseconds of the last forced probe request.
    private volatile long mLastProbeTimeMs;
    private int mNumSolicits;
    private int mInterSolicitIntervalMs;

    public IpReachabilityMonitor(
            Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
            boolean usingMultinetworkPolicyTracker) {
            boolean usingMultinetworkPolicyTracker, final INetd netd) {
        this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker,
                Dependencies.makeDefault(context, ifParams.name));
                Dependencies.makeDefault(context, ifParams.name), netd);
    }

    @VisibleForTesting
    IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log,
            Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) {
            Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies,
            final INetd netd) {
        if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");

        mContext = context;
        mInterfaceParams = ifParams;
        mLog = log.forSubComponent(TAG);
        mCallback = callback;
        mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
        mCm = context.getSystemService(ConnectivityManager.class);
        mDependencies = dependencies;
        mNetd = netd;
        Preconditions.checkNotNull(mNetd);
        Preconditions.checkArgument(!TextUtils.isEmpty(mInterfaceParams.name));

        // In case the overylaid parameters specify an invalid configuration, set the parameters
        // to the hardcoded defaults first, then set them to the values used in the steady state.
        try {
            setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS);
        } catch (Exception e) {
            Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults");
        }
        setNeighbourParametersForSteadyState();

        mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
                (NeighborEvent event) -> {
@@ -204,6 +235,8 @@ public class IpReachabilityMonitor {
                    if (event.nudState == StructNdMsg.NUD_FAILED) {
                        mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
                        handleNeighborLost(event);
                    } else if (event.nudState == StructNdMsg.NUD_REACHABLE) {
                        maybeRestoreNeighborParameters();
                    }
                });
        mIpNeighborMonitor.start();
@@ -331,13 +364,29 @@ public class IpReachabilityMonitor {
        logNudFailed(lostProvisioning);
    }

    private void maybeRestoreNeighborParameters() {
        for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
            if (DBG) {
                Log.d(TAG, "neighbour IPv4(v6): " + entry.getKey() + " neighbour state: "
                        + StructNdMsg.stringForNudState(entry.getValue().nudState));
            }
            if (entry.getValue().nudState != StructNdMsg.NUD_REACHABLE) return;
        }

        // All neighbours in the watchlist are in REACHABLE state and connection is stable,
        // restore NUD probe parameters to steadystate value. In the case where neighbours
        // are responsive, this code will run before the wakelock expires.
        setNeighbourParametersForSteadyState();
    }

    private boolean avoidingBadLinks() {
        return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi();
    }

    public void probeAll() {
        final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
        setNeighbourParametersPostRoaming();

        final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
        if (!ipProbeList.isEmpty()) {
            // Keep the CPU awake long enough to allow all ARP/ND
            // probes a reasonable chance at success. See b/23197666.
@@ -357,20 +406,51 @@ public class IpReachabilityMonitor {
        mLastProbeTimeMs = SystemClock.elapsedRealtime();
    }

    private static long getProbeWakeLockDuration() {
        // Ideally, this would be computed by examining the values of:
        //
        //     /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
        //
        // and:
        //
        //     /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms
        //
        // For now, just make some assumptions.
        final long numUnicastProbes = 3;
        final long retransTimeMs = 1000;
    private long getProbeWakeLockDuration() {
        final long gracePeriodMs = 500;
        return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
        return (long) (mNumSolicits * mInterSolicitIntervalMs) + gracePeriodMs;
    }

    private void setNeighbourParametersPostRoaming() {
        setNeighborParametersFromResources(R.integer.config_nud_postroaming_solicit_num,
                R.integer.config_nud_postroaming_solicit_interval);
    }

    private void setNeighbourParametersForSteadyState() {
        setNeighborParametersFromResources(R.integer.config_nud_steadystate_solicit_num,
                R.integer.config_nud_steadystate_solicit_interval);
    }

    private void setNeighborParametersFromResources(final int numResId, final int intervalResId) {
        try {
            final int numSolicits = mContext.getResources().getInteger(numResId);
            final int interSolicitIntervalMs = mContext.getResources().getInteger(intervalResId);
            setNeighborParameters(numSolicits, interSolicitIntervalMs);
        } catch (Exception e) {
            Log.e(TAG, "Failed to adjust neighbor parameters");
        }
    }

    private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs)
            throws RemoteException, IllegalArgumentException {
        Preconditions.checkArgument(numSolicits >= MIN_NUD_SOLICIT_NUM,
                "numSolicits must be at least " + MIN_NUD_SOLICIT_NUM);
        Preconditions.checkArgument(numSolicits <= MAX_NUD_SOLICIT_NUM,
                "numSolicits must be at most " + MAX_NUD_SOLICIT_NUM);
        Preconditions.checkArgument(interSolicitIntervalMs >= MIN_NUD_SOLICIT_INTERVAL_MS,
                "interSolicitIntervalMs must be at least " + MIN_NUD_SOLICIT_INTERVAL_MS);
        Preconditions.checkArgument(interSolicitIntervalMs <= MAX_NUD_SOLICIT_INTERVAL_MS,
                "interSolicitIntervalMs must be at most " + MAX_NUD_SOLICIT_INTERVAL_MS);

        for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
            mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "retrans_time_ms",
                    Integer.toString(interSolicitIntervalMs));
            mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "ucast_solicit",
                    Integer.toString(numSolicits));
        }

        mNumSolicits = numSolicits;
        mInterSolicitIntervalMs = interSolicitIntervalMs;
    }

    private void logEvent(int probeType, int errorCode) {
+3 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.net.INetd;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -40,11 +41,11 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpReachabilityMonitorTest {

    @Mock IpReachabilityMonitor.Callback mCallback;
    @Mock IpReachabilityMonitor.Dependencies mDependencies;
    @Mock SharedLog mLog;
    @Mock Context mContext;
    @Mock INetd mNetd;
    Handler mHandler;

    @Before
@@ -57,7 +58,7 @@ public class IpReachabilityMonitorTest {
    IpReachabilityMonitor makeMonitor() {
        final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
        return new IpReachabilityMonitor(
                mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies);
                mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies, mNetd);
    }

    @Test