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

Commit 39511688 authored by Xiao Ma's avatar Xiao Ma
Browse files

Move stopping DhcpClient to IpClient.StoppingState#enter.

This approach makes IpClient transit to StoppedState from StoppingState
smoothly regardless of the previous state. For example, when FILS
feature is enabled and IpClient receives provisioning timeout event
prior to preconnection abort message from wifi(e.g., L2 authentcation
timeout fies due to a wrong certification, which might take at least
1 min), then IpClient will stays at StoppingState and won't transit to
StoppedState, which also causes IpClient ignores the subsequent
CMD_START.

Moving stopping DhcpClient to StoppingState#enter() ensures IpClient
stop DhcpClient once it enters StoppingState, and quitting RunningState
is not a prerequisite any more. But also this fix doesn't change the
behavior for non-FILS cases:

- receive CMD_STOP at StartedState:
    - transitionToStoppingState() from StartedState, if mDhcpClient is
      null, just jump to StoppedState from StoppingState; if mDhcpClient
      isn't null(for example, from PreconnectingState), then IpClient
      stops DhcpClient at StoppingState and transits to StoppedState
      when receving DhcpClient.CMD_ON_QUIT.

- receive EVENT_PROVISIONING_TIMEOUT at StartedState:
    - transitionToStoppingState() from handleProvisioningFailure(),
      process is the same as above.

- receive DhcpClient.DHCP_FAILURE at RunningState:
    - transitionToStoppingState() from handleIPv4Failure(),
      StoppingState#enter() follows RunningState#exit() immediately.

- receive DhcpClient.CMD_CONFIGURE_LINKADDRESS at RunningState:
    - transitionToStoppingState() from RunningState when failed to
      set IPv4 address, StoppingState#enter() follows RunningState#exit()
      immediately.

- receive EVENT_NETLINK_LINKPROPERTIES_CHANGED at RunningState:
    - transitionToStoppingState() from RunningState when provisioning
      failed or user switched to another AP, StoppingState#enter() just
      follows RunningState#exit() immediately.

- receive CMD_STOP at RunningState:
    - StoppingState#enter() follows RunningState#exit() immediately.

Bug: 191938960
Test: atest NetworkStackIntegrationTests
Change-Id: I0b7bc32c50f80e96fa82ae39191b0c50241dd1ff
parent be631ce4
Loading
Loading
Loading
Loading
+3 −5
Original line number Diff line number Diff line
@@ -2001,6 +2001,9 @@ public class IpClient extends StateMachine {
            if (mDhcpClient == null) {
                // There's no DHCPv4 for which to wait; proceed to stopped.
                deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
            } else {
                mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
                mDhcpClient.doQuit();
            }

            // Restore the interface MTU to initial value if it has changed.
@@ -2314,11 +2317,6 @@ public class IpClient extends StateMachine {
                mIpReachabilityMonitor = null;
            }

            if (mDhcpClient != null) {
                mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
                mDhcpClient.doQuit();
            }

            if (mPacketTracker != null) {
                mPacketTracker.stop();
                mPacketTracker = null;
+46 −0
Original line number Diff line number Diff line
@@ -2074,6 +2074,52 @@ public abstract class IpClientIntegrationTestCommon {
        verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
    }

    @Test
    @SignatureRequiredTest(reason = "needs mocked alarm and access to IpClient handler thread")
    public void testDhcpClientPreconnection_DelayedAbortAndTransitToStoppedState()
            throws Exception {
        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
                .withPreconnection()
                .build();
        setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
                false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
        startIpClientProvisioning(config);
        assertDiscoverPacketOnPreconnectionStart();

        // IpClient is in the PreconnectingState, simulate provisioning timeout event
        // and force IpClient state machine transit to StoppingState.
        final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
        final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 18,
                mIpc.getHandler());
        mIpc.getHandler().post(() -> alarm.onAlarm());

        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
        final LinkProperties lp = captor.getValue();
        assertNotNull(lp);
        assertEquals(mIfaceName, lp.getInterfaceName());
        assertEquals(0, lp.getLinkAddresses().size());
        assertEquals(0, lp.getRoutes().size());
        assertEquals(0, lp.getMtu());
        assertEquals(0, lp.getDnsServers().size());

        // Send preconnection abort message, but IpClient should ignore it at this moment and
        // transit to StoppedState finally.
        mIpc.notifyPreconnectionComplete(false /* abort */);
        mIpc.stop();
        HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);

        reset(mCb);

        // Start provisioning again to verify IpClient can process CMD_START correctly at
        // StoppedState.
        startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
                false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
                false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
        final DhcpPacket discover = getNextDhcpPacket();
        assertTrue(discover instanceof DhcpDiscoverPacket);
    }

    @Test
    public void testDhcpDecline_conflictByArpReply() throws Exception {
        doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,