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

Commit 703b0974 authored by Erik Kline's avatar Erik Kline
Browse files

Ensure known good state when starting.

Split StartedState into StartedState and RunningState, and ensure
known good state before proceeding from the former to the latter.

Bug: 30290336
Change-Id: I0a93f8fe53c65a0b90c28c3cf708792146a92aab
parent aa1e49c1
Loading
Loading
Loading
Loading
+89 −20
Original line number Diff line number Diff line
@@ -382,6 +382,7 @@ public class IpManager extends StateMachine {
    private final State mStoppedState = new StoppedState();
    private final State mStoppingState = new StoppingState();
    private final State mStartedState = new StartedState();
    private final State mRunningState = new RunningState();

    private final String mTag;
    private final Context mContext;
@@ -476,6 +477,7 @@ public class IpManager extends StateMachine {
        // Super simple StateMachine.
        addState(mStoppedState);
        addState(mStartedState);
            addState(mRunningState, mStartedState);
        addState(mStoppingState);

        setInitialState(mStoppedState);
@@ -570,7 +572,7 @@ public class IpManager extends StateMachine {
        pw.decreaseIndent();

        pw.println();
        pw.println("StateMachine dump:");
        pw.println(mTag + " StateMachine dump:");
        pw.increaseIndent();
        mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
        pw.decreaseIndent();
@@ -768,6 +770,11 @@ public class IpManager extends StateMachine {
        //         - IPv6 addresses
        //         - IPv6 routes
        //         - IPv6 DNS servers
        //
        // N.B.: this is fundamentally race-prone and should be fixed by
        // changing NetlinkTracker from a hybrid edge/level model to an
        // edge-only model, or by giving IpManager its own netlink socket(s)
        // so as to track all required information directly.
        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
@@ -939,17 +946,31 @@ public class IpManager extends StateMachine {
        return true;
    }

    private void stopAllIP() {
        // We don't need to worry about routes, just addresses, because:
        //     - disableIpv6() will clear autoconf IPv6 routes as well, and
        //     - we don't get IPv4 routes from netlink
        // so we neither react to nor need to wait for changes in either.

    class StoppedState extends State {
        @Override
        public void enter() {
        try {
            mNwService.disableIpv6(mInterfaceName);
        } catch (Exception e) {
            Log.e(mTag, "Failed to disable IPv6" + e);
        }

        try {
            mNwService.clearInterfaceAddresses(mInterfaceName);
        } catch (Exception e) {
                Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
            Log.e(mTag, "Failed to clear addresses " + e);
        }
    }


    class StoppedState extends State {
        @Override
        public void enter() {
            stopAllIP();

            resetLinkProperties();
            if (mStartTimeMillis > 0) {
                recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
@@ -1023,12 +1044,71 @@ public class IpManager extends StateMachine {
    }

    class StartedState extends State {
        private boolean mDhcpActionInFlight;

        @Override
        public void enter() {
            mStartTimeMillis = SystemClock.elapsedRealtime();

            if (mConfiguration.mProvisioningTimeoutMs > 0) {
                final long alarmTime = SystemClock.elapsedRealtime() +
                        mConfiguration.mProvisioningTimeoutMs;
                mProvisioningTimeoutAlarm.schedule(alarmTime);
            }

            if (readyToProceed()) {
                transitionTo(mRunningState);
            } else {
                // Clear all IPv4 and IPv6 before proceeding to RunningState.
                // Clean up any leftover state from an abnormal exit from
                // tethering or during an IpManager restart.
                stopAllIP();
            }
        }

        @Override
        public void exit() {
            mProvisioningTimeoutAlarm.cancel();
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_STOP:
                    transitionTo(mStoppingState);
                    break;

                case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
                    handleLinkPropertiesUpdate(NO_CALLBACKS);
                    if (readyToProceed()) {
                        transitionTo(mRunningState);
                    }
                    break;

                case EVENT_PROVISIONING_TIMEOUT:
                    handleProvisioningFailure();
                    break;

                default:
                    // It's safe to process messages out of order because the
                    // only message that can both
                    //     a) be received at this time and
                    //     b) affect provisioning state
                    // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
                    deferMessage(msg);
            }
            return HANDLED;
        }

        boolean readyToProceed() {
            return (!mLinkProperties.hasIPv4Address() &&
                    !mLinkProperties.hasGlobalIPv6Address());
        }
    }

    class RunningState extends State {
        private boolean mDhcpActionInFlight;

        @Override
        public void enter() {
            mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
                    mCallback, mMulticastFiltering);
            // TODO: investigate the effects of any multicast filtering racing/interfering with the
@@ -1037,12 +1117,6 @@ public class IpManager extends StateMachine {
                mCallback.setFallbackMulticastFilter(mMulticastFiltering);
            }

            if (mConfiguration.mProvisioningTimeoutMs > 0) {
                final long alarmTime = SystemClock.elapsedRealtime() +
                        mConfiguration.mProvisioningTimeoutMs;
                mProvisioningTimeoutAlarm.schedule(alarmTime);
            }

            if (mConfiguration.mEnableIPv6) {
                // TODO: Consider transitionTo(mStoppingState) if this fails.
                startIPv6();
@@ -1070,7 +1144,6 @@ public class IpManager extends StateMachine {

        @Override
        public void exit() {
            mProvisioningTimeoutAlarm.cancel();
            stopDhcpAction();

            if (mIpReachabilityMonitor != null) {
@@ -1167,10 +1240,6 @@ public class IpManager extends StateMachine {
                    break;
                }

                case EVENT_PROVISIONING_TIMEOUT:
                    handleProvisioningFailure();
                    break;

                case EVENT_DHCPACTION_TIMEOUT:
                    stopDhcpAction();
                    break;