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

Commit f94dc187 authored by Erik Kline's avatar Erik Kline Committed by Android Partner Code Review
Browse files

Merge "Incorporate historical WifiStateMachine notions of provisioning." into mm-wireless-dev

parents d9c4bc0c 990fc496
Loading
Loading
Loading
Loading
+199 −44
Original line number Diff line number Diff line
@@ -92,13 +92,18 @@ public class IpManager extends StateMachine {
         */

        // Implementations must call IpManager#completedPreDhcpAction().
        // TODO: Remove this requirement, perhaps via some
        // registerForPreDhcpAction()-style mechanism.
        public void onPreDhcpAction() {}
        public void onPostDhcpAction() {}

        // TODO: Kill with fire once DHCP and static configuration are moved
        // out of WifiStateMachine.
        public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
        public void onIPv4ProvisioningFailure() {}
        // This is purely advisory and not an indication of provisioning
        // success or failure.  This is only here for callers that want to
        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
        // DHCPv4 or static IPv4 configuration failure or success can be
        // determined by whether or not the passed-in DhcpResults object is
        // null or not.
        public void onNewDhcpResults(DhcpResults dhcpResults) {}

        public void onProvisioningSuccess(LinkProperties newLp) {}
        public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,6 +127,7 @@ public class IpManager extends StateMachine {

    private final Object mLock = new Object();
    private final State mStoppedState = new StoppedState();
    private final State mStoppingState = new StoppingState();
    private final State mStartedState = new StartedState();

    private final Context mContext;
@@ -179,6 +185,8 @@ public class IpManager extends StateMachine {
        // Super simple StateMachine.
        addState(mStoppedState);
        addState(mStartedState);
        addState(mStoppingState);

        setInitialState(mStoppedState);
        setLogRecSize(MAX_LOG_RECORDS);
        super.start();
@@ -203,13 +211,11 @@ public class IpManager extends StateMachine {

    public void startProvisioning(StaticIpConfiguration staticIpConfig) {
        getInterfaceIndex();

        sendMessage(CMD_START, staticIpConfig);
    }

    public void startProvisioning() {
        getInterfaceIndex();

        sendMessage(CMD_START);
    }

@@ -236,6 +242,42 @@ public class IpManager extends StateMachine {
     * Internals.
     */

    @Override
    protected String getWhatToString(int what) {
        // TODO: Investigate switching to reflection.
        switch (what) {
            case CMD_STOP:
                return "CMD_STOP";
            case CMD_START:
                return "CMD_START";
            case CMD_CONFIRM:
                return "CMD_CONFIRM";
            case EVENT_PRE_DHCP_ACTION_COMPLETE:
                return "EVENT_PRE_DHCP_ACTION_COMPLETE";
            case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
                return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                return "DhcpStateMachine.CMD_PRE_DHCP_ACTION";
            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                return "DhcpStateMachine.CMD_POST_DHCP_ACTION";
            case DhcpStateMachine.CMD_ON_QUIT:
                return "DhcpStateMachine.CMD_ON_QUIT";
        }
        return "UNKNOWN:" + Integer.toString(what);
    }

    @Override
    protected String getLogRecString(Message msg) {
        final String logLine = String.format(
                "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
                mInterfaceName, mInterfaceIndex,
                msg.arg1, msg.arg2, Objects.toString(msg.obj));
        if (VDBG) {
            Log.d(TAG, getWhatToString(msg.what) + " " + logLine);
        }
        return logLine;
    }

    private void getInterfaceIndex() {
        try {
            mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
@@ -260,16 +302,93 @@ public class IpManager extends StateMachine {
        }
    }

    // For now: use WifiStateMachine's historical notion of provisioned.
    private static boolean isProvisioned(LinkProperties lp) {
        // For historical reasons, we should connect even if all we have is
        // an IPv4 address and nothing else.
        return lp.isProvisioned() || lp.hasIPv4Address();
    }

    // TODO: Investigate folding all this into the existing static function
    // LinkProperties.compareProvisioning() or some other single function that
    // takes two LinkProperties objects and returns a ProvisioningChange
    // object that is a correct and complete assessment of what changed, taking
    // account of the asymmetries described in the comments in this function.
    // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
    private static ProvisioningChange compareProvisioning(
            LinkProperties oldLp, LinkProperties newLp) {
        ProvisioningChange delta;

        final boolean wasProvisioned = isProvisioned(oldLp);
        final boolean isProvisioned = isProvisioned(newLp);

        if (!wasProvisioned && isProvisioned) {
            delta = ProvisioningChange.GAINED_PROVISIONING;
        } else if (wasProvisioned && isProvisioned) {
            delta = ProvisioningChange.STILL_PROVISIONED;
        } else if (!wasProvisioned && !isProvisioned) {
            delta = ProvisioningChange.STILL_NOT_PROVISIONED;
        } else {
            // (wasProvisioned && !isProvisioned)
            //
            // Note that this is true even if we lose a configuration element
            // (e.g., a default gateway) that would not be required to advance
            // into provisioned state. This is intended: if we have a default
            // router and we lose it, that's a sure sign of a problem, but if
            // we connect to a network with no IPv4 DNS servers, we consider
            // that to be a network without DNS servers and connect anyway.
            //
            // See the comment below.
            delta = ProvisioningChange.LOST_PROVISIONING;
        }

        // Additionally:
        //
        // Partial configurations (e.g., only an IPv4 address with no DNS
        // servers and no default route) are accepted as long as DHCPv4
        // succeeds. On such a network, isProvisioned() will always return
        // false, because the configuration is not complete, but we want to
        // connect anyway. It might be a disconnected network such as a
        // Chromecast or a wireless printer, for example.
        //
        // Because on such a network isProvisioned() will always return false,
        // delta will never be LOST_PROVISIONING. So check for loss of
        // provisioning here too.
        if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
                (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
            delta = ProvisioningChange.LOST_PROVISIONING;
        }

        return delta;
    }

    private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
        switch (delta) {
            case GAINED_PROVISIONING:
                if (VDBG) { Log.d(TAG, "onProvisioningSuccess()"); }
                mCallback.onProvisioningSuccess(newLp);
                break;

            case LOST_PROVISIONING:
                if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); }
                mCallback.onProvisioningFailure(newLp);
                break;

            default:
                if (VDBG) { Log.d(TAG, "onLinkPropertiesChange()"); }
                mCallback.onLinkPropertiesChange(newLp);
                break;
        }
    }

    private ProvisioningChange setLinkProperties(LinkProperties newLp) {
        if (mIpReachabilityMonitor != null) {
            mIpReachabilityMonitor.updateLinkProperties(newLp);
        }

        // TODO: Figure out whether and how to incorporate static configuration
        // into the notion of provisioning.
        ProvisioningChange delta;
        synchronized (mLock) {
            delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
            delta = compareProvisioning(mLinkProperties, newLp);
            mLinkProperties = new LinkProperties(newLp);
        }

@@ -351,15 +470,45 @@ public class IpManager extends StateMachine {

    private void handleIPv4Success(DhcpResults dhcpResults) {
        mDhcpResults = new DhcpResults(dhcpResults);
        setLinkProperties(assembleLinkProperties());
        mCallback.onIPv4ProvisioningSuccess(dhcpResults);
        final LinkProperties newLp = assembleLinkProperties();
        final ProvisioningChange delta = setLinkProperties(newLp);

        if (VDBG) {
            Log.d(TAG, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
        }
        mCallback.onNewDhcpResults(dhcpResults);

        dispatchCallback(delta, newLp);
    }

    private void handleIPv4Failure() {
        // TODO: Figure out to de-dup this and the same code in DhcpClient.
        clearIPv4Address();
        mDhcpResults = null;
        setLinkProperties(assembleLinkProperties());
        mCallback.onIPv4ProvisioningFailure();
        final LinkProperties newLp = assembleLinkProperties();
        ProvisioningChange delta = setLinkProperties(newLp);
        // If we've gotten here and we're still not provisioned treat that as
        // a total loss of provisioning.
        //
        // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
        // there was no usable IPv6 obtained before the DHCPv4 timeout.
        //
        // Regardless: GAME OVER.
        //
        // TODO: Make the DHCP client not time out and just continue in
        // exponential backoff. Callers such as Wi-Fi which need a timeout
        // should implement it themselves.
        if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
            delta = ProvisioningChange.LOST_PROVISIONING;
        }

        if (VDBG) { Log.d(TAG, "onNewDhcpResults(null)"); }
        mCallback.onNewDhcpResults(null);

        dispatchCallback(delta, newLp);
        if (delta == ProvisioningChange.LOST_PROVISIONING) {
            transitionTo(mStoppingState);
        }
    }

    class StoppedState extends State {
@@ -391,13 +540,8 @@ public class IpManager extends StateMachine {
                    break;

                case DhcpStateMachine.CMD_ON_QUIT:
                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
                    // Shutting down DHCPv4 progresses simultaneously with
                    // transitioning to StoppedState, so we can receive this
                    // message after we've already transitioned here.
                    //
                    // TODO: Figure out if this is actually useful and if not
                    // expunge it.
                    // Everything is already stopped.
                    Log.e(TAG, "Unexpected CMD_ON_QUIT (already stopped).");
                    break;

                default:
@@ -407,6 +551,30 @@ public class IpManager extends StateMachine {
        }
    }

    class StoppingState extends State {
        @Override
        public void enter() {
            if (mDhcpStateMachine == null) {
                // There's no DHCPv4 for which to wait; proceed to stopped.
                transitionTo(mStoppedState);
            }
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case DhcpStateMachine.CMD_ON_QUIT:
                    mDhcpStateMachine = null;
                    transitionTo(mStoppedState);
                    break;

                default:
                    deferMessage(msg);
            }
            return HANDLED;
        }
    }

    class StartedState extends State {
        @Override
        public void enter() {
@@ -439,7 +607,9 @@ public class IpManager extends StateMachine {
                if (applyStaticIpConfig()) {
                    handleIPv4Success(new DhcpResults(mStaticIpConfig));
                } else {
                    handleIPv4Failure();
                    if (VDBG) { Log.d(TAG, "onProvisioningFailure()"); }
                    mCallback.onProvisioningFailure(getLinkProperties());
                    transitionTo(mStoppingState);
                }
            } else {
                // Start DHCPv4.
@@ -457,7 +627,6 @@ public class IpManager extends StateMachine {
            if (mDhcpStateMachine != null) {
                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
                mDhcpStateMachine.doQuit();
                mDhcpStateMachine = null;
            }

            resetLinkProperties();
@@ -500,28 +669,15 @@ public class IpManager extends StateMachine {
                        break;
                    }
                    final ProvisioningChange delta = setLinkProperties(newLp);

                    // NOTE: The only receiver of these callbacks currently
                    // treats all three of them identically, namely it calls
                    // IpManager#getLinkProperties() and makes its own determination.
                    switch (delta) {
                        case GAINED_PROVISIONING:
                            mCallback.onProvisioningSuccess(newLp);
                            break;

                        case LOST_PROVISIONING:
                            mCallback.onProvisioningFailure(newLp);
                            break;

                        default:
                            // TODO: Only notify on STILL_PROVISIONED?
                            mCallback.onLinkPropertiesChange(newLp);
                            break;
                    dispatchCallback(delta, newLp);
                    if (delta == ProvisioningChange.LOST_PROVISIONING) {
                        transitionTo(mStoppedState);
                    }
                    break;
                }

                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                    if (VDBG) { Log.d(TAG, "onPreDhcpAction()"); }
                    mCallback.onPreDhcpAction();
                    break;

@@ -529,6 +685,7 @@ public class IpManager extends StateMachine {
                    // Note that onPostDhcpAction() is likely to be
                    // asynchronous, and thus there is no guarantee that we
                    // will be able to observe any of its effects here.
                    if (VDBG) { Log.d(TAG, "onPostDhcpAction()"); }
                    mCallback.onPostDhcpAction();

                    final DhcpResults dhcpResults = (DhcpResults) msg.obj;
@@ -546,11 +703,9 @@ public class IpManager extends StateMachine {
                }

                case DhcpStateMachine.CMD_ON_QUIT:
                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
                    // Regardless, we ignore it.
                    //
                    // TODO: Figure out if this is actually useful and if not
                    // expunge it.
                    // DHCPv4 quit early for some reason.
                    Log.e(TAG, "Unexpected CMD_ON_QUIT.");
                    mDhcpStateMachine = null;
                    break;

                default: