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

Commit 02434dac authored by Xiao Ma's avatar Xiao Ma Committed by android-build-merger
Browse files

Merge "Add DHCP INIT-REBOOT state in DHCP client."

am: 670a869a

Change-Id: I9f4eb3e0f67fe16ac64e3aff5b1af8b84aab0a73
parents 8679e1eb 670a869a
Loading
Loading
Loading
Loading
+197 −10
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
import static android.net.util.NetworkStackUtils.closeSocketQuietly;
import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_INET;
@@ -43,11 +44,16 @@ import static android.system.OsConstants.SO_REUSEADDR;

import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;

import android.annotation.NonNull;
import android.content.Context;
import android.net.DhcpResults;
import android.net.InetAddresses;
import android.net.NetworkStackIpMemoryStore;
import android.net.TrafficStats;
import android.net.ip.IpClient;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
import android.net.ipmemorystore.OnStatusListener;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.IpConnectivityLog;
@@ -62,6 +68,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
@@ -120,6 +127,7 @@ public class DhcpClient extends StateMachine {
    private static final int SECONDS = 1000;
    private static final int FIRST_TIMEOUT_MS         =   2 * SECONDS;
    private static final int MAX_TIMEOUT_MS           = 128 * SECONDS;
    private static final int IPMEMORYSTORE_TIMEOUT_MS =   1 * SECONDS;

    // This is not strictly needed, since the client is asynchronous and implements exponential
    // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
@@ -166,6 +174,12 @@ public class DhcpClient extends StateMachine {
    private static final int CMD_RENEW_DHCP       = PRIVATE_BASE + 4;
    private static final int CMD_REBIND_DHCP      = PRIVATE_BASE + 5;
    private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 6;
    private static final int EVENT_CONFIGURATION_TIMEOUT   = PRIVATE_BASE + 7;
    private static final int EVENT_CONFIGURATION_OBTAINED  = PRIVATE_BASE + 8;
    private static final int EVENT_CONFIGURATION_INVALID   = PRIVATE_BASE + 9;

    // constant to represent this DHCP lease has been expired.
    private static final long EXPIRED_LEASE = 1L;

    // For message logging.
    private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -222,6 +236,12 @@ public class DhcpClient extends StateMachine {
    private DhcpResults mDhcpLease;
    private long mDhcpLeaseExpiry;
    private DhcpResults mOffer;
    private String mL2Key;
    private Inet4Address mLastAssignedIpv4Address;
    private long mLastAssignedIpv4AddressExpiry;
    private boolean mDhcpLeaseCacheEnabled;
    @NonNull
    private final NetworkStackIpMemoryStore mIpMemoryStore;

    // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
    private long mLastInitEnterTime;
@@ -240,8 +260,11 @@ public class DhcpClient extends StateMachine {
    private State mDhcpRebindingState = new DhcpRebindingState();
    private State mDhcpInitRebootState = new DhcpInitRebootState();
    private State mDhcpRebootingState = new DhcpRebootingState();
    private State mObtainingConfigurationState = new ObtainingConfigurationState();
    private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
    private State mWaitBeforeObtainingConfigurationState =
            new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);

    private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
        cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
@@ -249,17 +272,22 @@ public class DhcpClient extends StateMachine {
    }

    // TODO: Take an InterfaceParams instance instead of an interface name String.
    private DhcpClient(Context context, StateMachine controller, String iface) {
    private DhcpClient(Context context, StateMachine controller, String iface,
            NetworkStackIpMemoryStore ipMemoryStore) {
        super(TAG, controller.getHandler());

        mContext = context;
        mController = controller;
        mIfaceName = iface;
        mIpMemoryStore = ipMemoryStore;

        // CHECKSTYLE:OFF IndentationCheck
        addState(mStoppedState);
        addState(mDhcpState);
            addState(mDhcpInitState, mDhcpState);
            addState(mWaitBeforeStartState, mDhcpState);
            addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
            addState(mObtainingConfigurationState, mDhcpState);
            addState(mDhcpSelectingState, mDhcpState);
            addState(mDhcpRequestingState, mDhcpState);
            addState(mDhcpHaveLeaseState, mDhcpState);
@@ -270,6 +298,7 @@ public class DhcpClient extends StateMachine {
                addState(mDhcpRebindingState, mDhcpHaveLeaseState);
            addState(mDhcpInitRebootState, mDhcpState);
            addState(mDhcpRebootingState, mDhcpState);
        // CHECKSTYLE:ON IndentationCheck

        setInitialState(mStoppedState);

@@ -290,13 +319,34 @@ public class DhcpClient extends StateMachine {
    }

    public static DhcpClient makeDhcpClient(
            Context context, StateMachine controller, InterfaceParams ifParams) {
        DhcpClient client = new DhcpClient(context, controller, ifParams.name);
            Context context, StateMachine controller, InterfaceParams ifParams,
            NetworkStackIpMemoryStore ipMemoryStore) {
        DhcpClient client = new DhcpClient(context, controller, ifParams.name, ipMemoryStore);
        client.mIface = ifParams;
        client.start();
        return client;
    }

    /**
     * check whether or not to support caching the last lease info and INIT-REBOOT state.
     *
     */
    public boolean isDhcpLeaseCacheEnabled() {
        // TODO: call DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY,
        //                    DeviceConfig.PROPERTY);
        // to fetch the dynamic experiment flag value. Return false by default.
        return mDhcpLeaseCacheEnabled;
    }

    /**
     * set DHCP lease cached enabled experiment flag.
     *
     */
    @VisibleForTesting
    public void setDhcpLeaseCacheEnabled(final boolean enabled) {
        mDhcpLeaseCacheEnabled = enabled;
    }

    private boolean initInterface() {
        if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
        if (mIface == null) {
@@ -491,12 +541,48 @@ public class DhcpClient extends StateMachine {
        Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
    }

    private void setLeaseExpiredToIpMemoryStore() {
        final String l2Key = mL2Key;
        if (l2Key == null) return;
        final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
        // TODO: clear out the address and lease instead of storing an expired lease
        na.setAssignedV4AddressExpiry(EXPIRED_LEASE);

        final OnStatusListener listener = status -> {
            if (!status.isSuccess()) Log.e(TAG, "Failed to set lease expiry, status: " + status);
        };
        mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
    }

    private void maybeSaveLeaseToIpMemoryStore() {
        final String l2Key = mL2Key;
        if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
        final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
        na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
        na.setAssignedV4AddressExpiry((mDhcpLease.leaseDuration == INFINITE_LEASE)
                ? Long.MAX_VALUE
                : mDhcpLease.leaseDuration * 1000 + System.currentTimeMillis());
        na.setDnsAddresses(mDhcpLease.dnsServers);
        na.setMtu(mDhcpLease.mtu);

        final OnStatusListener listener = status -> {
            if (!status.isSuccess()) Log.e(TAG, "Failed to store network attrs, status: " + status);
        };
        mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
    }

    private void notifySuccess() {
        if (isDhcpLeaseCacheEnabled()) {
            maybeSaveLeaseToIpMemoryStore();
        }
        mController.sendMessage(
                CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
    }

    private void notifyFailure() {
        if (isDhcpLeaseCacheEnabled()) {
            setLeaseExpiredToIpMemoryStore();
        }
        mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
    }

@@ -616,10 +702,13 @@ public class DhcpClient extends StateMachine {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START_DHCP:
                    mL2Key = (String) message.obj;
                    if (mRegisteredForPreDhcpNotification) {
                        transitionTo(mWaitBeforeStartState);
                        transitionTo(isDhcpLeaseCacheEnabled()
                                ? mWaitBeforeObtainingConfigurationState : mWaitBeforeStartState);
                    } else {
                        transitionTo(mDhcpInitState);
                        transitionTo(isDhcpLeaseCacheEnabled()
                                ? mObtainingConfigurationState : mDhcpInitState);
                    }
                    return HANDLED;
                default:
@@ -629,14 +718,21 @@ public class DhcpClient extends StateMachine {
    }

    class WaitBeforeStartState extends WaitBeforeOtherState {
        public WaitBeforeStartState(State otherState) {
        WaitBeforeStartState(State otherState) {
            super();
            mOtherState = otherState;
        }
    }

    class WaitBeforeRenewalState extends WaitBeforeOtherState {
        public WaitBeforeRenewalState(State otherState) {
        WaitBeforeRenewalState(State otherState) {
            super();
            mOtherState = otherState;
        }
    }

    class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
        WaitBeforeObtainingConfigurationState(State otherState) {
            super();
            mOtherState = otherState;
        }
@@ -782,6 +878,69 @@ public class DhcpClient extends StateMachine {
        }
    }

    class ObtainingConfigurationState extends LoggingState {
        @Override
        public void enter() {
            super.enter();

            // Set a timeout for retrieving network attributes operation
            sendMessageDelayed(EVENT_CONFIGURATION_TIMEOUT, IPMEMORYSTORE_TIMEOUT_MS);

            final OnNetworkAttributesRetrievedListener listener = (status, l2Key, attributes) -> {
                if (null == attributes || null == attributes.assignedV4Address) {
                    if (!status.isSuccess()) {
                        Log.e(TAG, "Error retrieving network attributes: " + status);
                    }
                    sendMessage(EVENT_CONFIGURATION_INVALID);
                    return;
                }
                sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
            };
            mIpMemoryStore.retrieveNetworkAttributes(mL2Key, listener);
        }

        @Override
        public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case EVENT_CONFIGURATION_INVALID:
                case EVENT_CONFIGURATION_TIMEOUT:
                    transitionTo(mDhcpInitState);
                    return HANDLED;

                case EVENT_CONFIGURATION_OBTAINED:
                    final long currentTime = System.currentTimeMillis();
                    NetworkAttributes attributes = (NetworkAttributes) message.obj;
                    if (DBG) {
                        Log.d(TAG, "l2key: " + mL2Key
                                + " lease address: " + attributes.assignedV4Address
                                + " lease expiry: "  + attributes.assignedV4AddressExpiry
                                + " current time: "  + currentTime);
                    }
                    if (currentTime >= attributes.assignedV4AddressExpiry) {
                        // Lease has expired.
                        transitionTo(mDhcpInitState);
                        return HANDLED;
                    }
                    mLastAssignedIpv4Address = attributes.assignedV4Address;
                    mLastAssignedIpv4AddressExpiry = attributes.assignedV4AddressExpiry;
                    transitionTo(mDhcpInitRebootState);
                    return HANDLED;

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

        @Override
        public void exit() {
            removeMessages(EVENT_CONFIGURATION_INVALID);
            removeMessages(EVENT_CONFIGURATION_TIMEOUT);
            removeMessages(EVENT_CONFIGURATION_OBTAINED);
        }
    }

    class DhcpInitState extends PacketRetransmittingState {
        public DhcpInitState() {
            super();
@@ -1050,7 +1209,35 @@ public class DhcpClient extends StateMachine {
        }
    }

    class DhcpInitRebootState extends LoggingState {
    class DhcpInitRebootState extends DhcpRequestingState {
        @Override
        public void enter() {
            super.enter();
            startNewTransaction();
        }

        // RFC 2131 4.3.2 describes generated DHCPREQUEST message during
        // INIT-REBOOT state:
        // 'server identifier' MUST NOT be filled in, 'requested IP address'
        // option MUST be filled in with client's notion of its previously
        // assigned address. 'ciaddr' MUST be zero. The client is seeking to
        // verify a previously allocated, cached configuration. Server SHOULD
        // send a DHCPNAK message to the client if the 'requested IP address'
        // is incorrect, or is on the wrong network.
        @Override
        protected boolean sendPacket() {
            return sendRequestPacket(
                     INADDR_ANY,                                       // ciaddr
                     mLastAssignedIpv4Address,                         // DHCP_REQUESTED_IP
                     null,                                             // DHCP_SERVER_IDENTIFIER
                     INADDR_BROADCAST);                                // packet destination address
        }

        @Override
        public void exit() {
            mLastAssignedIpv4Address = null;
            mLastAssignedIpv4AddressExpiry = 0;
        }
    }

    class DhcpRebootingState extends LoggingState {
+13 −7
Original line number Diff line number Diff line
@@ -78,7 +78,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;


/**
 * IpClient
 *
@@ -394,6 +393,14 @@ public class IpClient extends StateMachine {
        public INetd getNetd(Context context) {
            return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
        }

        /**
         * Get a IpMemoryStore instance.
         */
        public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
                NetworkStackServiceManager nssManager) {
            return new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
        }
    }

    public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -418,8 +425,7 @@ public class IpClient extends StateMachine {
        mShutdownLatch = new CountDownLatch(1);
        mCm = mContext.getSystemService(ConnectivityManager.class);
        mObserverRegistry = observerRegistry;
        mIpMemoryStore =
                new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
        mIpMemoryStore = deps.getIpMemoryStore(context, nssManager);

        sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
        mLog = sSmLogs.get(mInterfaceName);
@@ -1124,6 +1130,7 @@ public class IpClient extends StateMachine {
        }
        mCallback.onNewDhcpResults(dhcpResults);
        maybeSaveNetworkToIpMemoryStore();

        dispatchCallback(delta, newLp);
    }

@@ -1182,9 +1189,10 @@ public class IpClient extends StateMachine {
            }
        } else {
            // Start DHCPv4.
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
                    mIpMemoryStore);
            mDhcpClient.registerForPreDhcpNotification();
            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, mL2Key);
        }

        return true;
@@ -1369,7 +1377,6 @@ public class IpClient extends StateMachine {
        @Override
        public void enter() {
            mStartTimeMillis = SystemClock.elapsedRealtime();

            if (mConfiguration.mProvisioningTimeoutMs > 0) {
                final long alarmTime = SystemClock.elapsedRealtime()
                        + mConfiguration.mProvisioningTimeoutMs;
@@ -1426,7 +1433,6 @@ public class IpClient extends StateMachine {
                case EVENT_PROVISIONING_TIMEOUT:
                    handleProvisioningFailure();
                    break;

                default:
                    // It's safe to process messages out of order because the
                    // only message that can both
+2 −1
Original line number Diff line number Diff line
@@ -73,7 +73,8 @@ public class IpMemoryStoreDatabase {
        public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";

        public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry";
        // The lease expiry timestamp in uint of milliseconds
        // The lease expiry timestamp in uint of milliseconds since the Epoch. Long.MAX_VALUE
        // is used to represent "infinite lease".
        public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT";

        // Please note that the group hint is only a *hint*, hence its name. The client can offer
+9 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.internal.R;
import com.android.server.NetworkObserver;
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;

import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * Tests for IpClient.
 */
@@ -98,6 +100,8 @@ public class IpClientTest {
    @Mock private ContentResolver mContentResolver;
    @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager;
    @Mock private NetworkStackIpMemoryStore mIpMemoryStore;
    @Mock private IpMemoryStoreService mIpMemoryStoreService;
    @Mock private InterfaceParams mInterfaceParams;

    private NetworkObserver mObserver;
    private InterfaceParams mIfParams;
@@ -113,6 +117,11 @@ public class IpClientTest {
        when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mNetworkStackServiceManager.getIpMemoryStoreService())
                .thenReturn(mIpMemoryStoreService);
        when(mDependencies.getInterfaceParams(any())).thenReturn(mInterfaceParams);
        when(mDependencies.getIpMemoryStore(mContext, mNetworkStackServiceManager))
                .thenReturn(mIpMemoryStore);

        mIfParams = null;
    }
+1 −1
Original line number Diff line number Diff line
@@ -224,7 +224,7 @@ public class IpMemoryStoreServiceTest {
        };
    }

    /** Helper method to make an IOnSameNetworkResponseListener */
    /** Helper method to make an IOnSameL3NetworkResponseListener */
    private interface OnSameL3NetworkResponseListener {
        void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
    }