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

Commit 846c80ac authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN Committed by Gerrit Code Review
Browse files

Merge "Switch DHCP server based on global setting"

parents 7aae4821 e3bb5c5a
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -1126,7 +1126,9 @@ public class Tethering extends BaseNetworkObserver {
    }

    public String[] getTetheredDhcpRanges() {
        return mConfig.dhcpRanges;
        // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used
        // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers.
        return mConfig.legacyDhcpRanges;
    }

    public String[] getErroredIfaces() {
@@ -1297,13 +1299,17 @@ public class Tethering extends BaseNetworkObserver {
                return false;
            }
            // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
            // Legacy DHCP server is disabled if passed an empty ranges array
            final String[] dhcpRanges = cfg.enableLegacyDhcpServer
                    ? cfg.legacyDhcpRanges
                    : new String[0];
            try {
                // TODO: Find a more accurate method name (startDHCPv4()?).
                mNMService.startTethering(cfg.dhcpRanges);
                mNMService.startTethering(dhcpRanges);
            } catch (Exception e) {
                try {
                    mNMService.stopTethering();
                    mNMService.startTethering(cfg.dhcpRanges);
                    mNMService.startTethering(dhcpRanges);
                } catch (Exception ee) {
                    mLog.e(ee);
                    transitionTo(mStartTetheringErrorState);
@@ -1972,7 +1978,7 @@ public class Tethering extends BaseNetworkObserver {
        final TetherState tetherState = new TetherState(
                new TetherInterfaceStateMachine(
                    iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
                    makeControlCallback(iface), mDeps));
                    makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps));
        mTetherStates.put(iface, tetherState);
        tetherState.stateMachine.start();
    }
+70 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.connectivity.tethering;

import static android.net.NetworkUtils.numericToInetAddress;
import static android.net.util.NetworkConstants.asByte;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
@@ -27,8 +28,9 @@ import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.InterfaceController;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
@@ -49,6 +51,7 @@ import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -73,6 +76,13 @@ public class TetherInterfaceStateMachine extends StateMachine {
    private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
    private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;

    // TODO: have PanService use some visible version of this constant
    private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
    private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;

    // TODO: have this configurable
    private static final int DHCP_LEASE_TIME_SECS = 3600;

    private final static String TAG = "TetherInterfaceSM";
    private final static boolean DBG = false;
    private final static boolean VDBG = false;
@@ -119,6 +129,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
    private final String mIfaceName;
    private final int mInterfaceType;
    private final LinkProperties mLinkProperties;
    private final boolean mUsingLegacyDhcp;

    private final TetheringDependencies mDeps;

@@ -134,12 +145,13 @@ public class TetherInterfaceStateMachine extends StateMachine {
    // Advertisements (otherwise, we do not add them to mLinkProperties at all).
    private LinkProperties mLastIPv6LinkProperties;
    private RouterAdvertisementDaemon mRaDaemon;
    private DhcpServer mDhcpServer;
    private RaParams mLastRaParams;

    public TetherInterfaceStateMachine(
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            INetworkManagementService nMService, INetworkStatsService statsService,
            IControlsTethering tetherController,
            IControlsTethering tetherController, boolean usingLegacyDhcp,
            TetheringDependencies deps) {
        super(ifaceName, looper);
        mLog = log.forSubComponent(ifaceName);
@@ -151,6 +163,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
        mIfaceName = ifaceName;
        mInterfaceType = interfaceType;
        mLinkProperties = new LinkProperties();
        mUsingLegacyDhcp = usingLegacyDhcp;
        mDeps = deps;
        resetLinkProperties();
        mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
@@ -188,6 +201,52 @@ public class TetherInterfaceStateMachine extends StateMachine {

    private boolean startIPv4() { return configureIPv4(true); }

    private boolean startDhcp(Inet4Address addr, int prefixLen) {
        if (mUsingLegacyDhcp) {
            return true;
        }

        final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName);
        if (ifaceParams == null) {
            Log.e(TAG, "Failed to find interface params for DHCPv4");
            return false;
        }
        final DhcpServingParams params;
        try {
            params = new DhcpServingParams.Builder()
                    .setDefaultRouters(addr)
                    .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
                    .setDnsServers(addr)
                    .setServerAddr(new LinkAddress(addr, prefixLen))
                    .build();
            // TODO: also advertise link MTU
        } catch (DhcpServingParams.InvalidParameterException e) {
            Log.e(TAG, "Invalid DHCP parameters", e);
            return false;
        }

        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params,
                mLog.forSubComponent("DHCP"));
        mDhcpServer.start();
        return true;
    }

    private void stopDhcp() {
        if (mDhcpServer != null) {
            mDhcpServer.stop();
            mDhcpServer = null;
        }
    }

    private boolean configureDhcp(boolean enable, Inet4Address addr, int prefixLen) {
        if (enable) {
            return startDhcp(addr, prefixLen);
        } else {
            stopDhcp();
            return true;
        }
    }

    private void stopIPv4() {
        configureIPv4(false);
        // NOTE: All of configureIPv4() will be refactored out of existence
@@ -210,8 +269,9 @@ public class TetherInterfaceStateMachine extends StateMachine {
            ipAsString = getRandomWifiIPv4Address();
            prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
        } else {
            // Nothing to do, BT does this elsewhere.
            return true;
            // BT configures the interface elsewhere: only start DHCP.
            final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR);
            return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
        }

        final LinkAddress linkAddr;
@@ -222,7 +282,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
                return false;
            }

            InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
            InetAddress addr = numericToInetAddress(ipAsString);
            linkAddr = new LinkAddress(addr, prefixLen);
            ifcg.setLinkAddress(linkAddr);
            if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
@@ -239,6 +299,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
            }
            ifcg.clearFlag("running");
            mNMService.setInterfaceConfig(mIfaceName, ifcg);

            if (!configureDhcp(enabled, (Inet4Address) addr, prefixLen)) {
                return false;
            }
        } catch (Exception e) {
            mLog.e("Error configuring interface " + e);
            return false;
@@ -258,7 +322,7 @@ public class TetherInterfaceStateMachine extends StateMachine {

    private String getRandomWifiIPv4Address() {
        try {
            byte[] bytes = NetworkUtils.numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
            byte[] bytes = numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
            bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
            return InetAddress.getByAddress(bytes).getHostAddress();
        } catch (Exception e) {
+20 −7
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;

import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
import static com.android.internal.R.array.config_tether_bluetooth_regexs;
import static com.android.internal.R.array.config_tether_dhcp_range;
@@ -30,15 +32,16 @@ import static com.android.internal.R.array.config_tether_wifi_regexs;
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.util.SharedLog;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -68,12 +71,13 @@ public class TetheringConfiguration {
    public static final int DUN_REQUIRED = 1;
    public static final int DUN_UNSPECIFIED = 2;

    // Default ranges used for the legacy DHCP server.
    // USB is  192.168.42.1 and 255.255.255.0
    // Wifi is 192.168.43.1 and 255.255.255.0
    // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
    // with 255.255.255.0
    // P2P is 192.168.49.1 and 255.255.255.0
    private static final String[] DHCP_DEFAULT_RANGE = {
    private static final String[] LEGACY_DHCP_DEFAULT_RANGE = {
        "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
        "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
        "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
@@ -89,8 +93,9 @@ public class TetheringConfiguration {
    public final boolean isDunRequired;
    public final boolean chooseUpstreamAutomatically;
    public final Collection<Integer> preferredUpstreamIfaceTypes;
    public final String[] dhcpRanges;
    public final String[] legacyDhcpRanges;
    public final String[] defaultIPv4DNS;
    public final boolean enableLegacyDhcpServer;

    public final String[] provisioningApp;
    public final String provisioningAppNoUi;
@@ -112,8 +117,9 @@ public class TetheringConfiguration {
        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
        isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);

        dhcpRanges = getDhcpRanges(ctx);
        legacyDhcpRanges = getLegacyDhcpRanges(ctx);
        defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
        enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);

        provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
        provisioningAppNoUi = getProvisioningAppNoUi(ctx);
@@ -150,7 +156,7 @@ public class TetheringConfiguration {
        dumpStringArray(pw, "preferredUpstreamIfaceTypes",
                preferredUpstreamNames(preferredUpstreamIfaceTypes));

        dumpStringArray(pw, "dhcpRanges", dhcpRanges);
        dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
        dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);

        dumpStringArray(pw, "provisioningApp", provisioningApp);
@@ -276,12 +282,12 @@ public class TetheringConfiguration {
        return false;
    }

    private static String[] getDhcpRanges(Context ctx) {
    private static String[] getLegacyDhcpRanges(Context ctx) {
        final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
        if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
            return fromResource;
        }
        return copy(DHCP_DEFAULT_RANGE);
        return copy(LEGACY_DHCP_DEFAULT_RANGE);
    }

    private static String getProvisioningAppNoUi(Context ctx) {
@@ -309,6 +315,13 @@ public class TetheringConfiguration {
        }
    }

    private static boolean getEnableLegacyDhcpServer(Context ctx) {
        // TODO: make the default false (0) and update javadoc in Settings.java
        final ContentResolver cr = ctx.getContentResolver();
        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
        return intVal != 0;
    }

    private static String[] copy(String[] strarray) {
        return Arrays.copyOf(strarray, strarray.length);
    }
+8 −0
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package com.android.server.connectivity.tethering;
import android.content.Context;
import android.net.INetd;
import android.net.NetworkRequest;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
import android.os.Handler;
import android.net.util.SharedLog;
import android.os.Looper;

import com.android.internal.util.StateMachine;

@@ -69,4 +72,9 @@ public class TetheringDependencies {
    public NetworkRequest getDefaultNetworkRequest() {
        return null;
    }

    public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params,
            SharedLog log) {
        return new DhcpServer(looper, iface, params, log);
    }
}
+78 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.connectivity.tethering;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -23,7 +24,9 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -40,9 +43,15 @@ import static com.android.server.connectivity.tethering.IControlsTethering.STATE

import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
@@ -58,6 +67,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -68,33 +78,58 @@ public class TetherInterfaceStateMachineTest {
    private static final String IFACE_NAME = "testnet1";
    private static final String UPSTREAM_IFACE = "upstream0";
    private static final String UPSTREAM_IFACE2 = "upstream1";
    private static final int DHCP_LEASE_TIME_SECS = 3600;

    private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
            IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);

    @Mock private INetworkManagementService mNMService;
    @Mock private INetworkStatsService mStatsService;
    @Mock private IControlsTethering mTetherHelper;
    @Mock private InterfaceConfiguration mInterfaceConfiguration;
    @Mock private SharedLog mSharedLog;
    @Mock private DhcpServer mDhcpServer;
    @Mock private RouterAdvertisementDaemon mRaDaemon;
    @Mock private TetheringDependencies mTetheringDependencies;

    @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;

    private final TestLooper mLooper = new TestLooper();
    private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
            ArgumentCaptor.forClass(LinkProperties.class);
    private TetherInterfaceStateMachine mTestedSm;

    private void initStateMachine(int interfaceType) throws Exception {
        initStateMachine(interfaceType, false /* usingLegacyDhcp */);
    }

    private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
        mTestedSm = new TetherInterfaceStateMachine(
                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
                mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
                mNMService, mStatsService, mTetherHelper, usingLegacyDhcp,
                mTetheringDependencies);
        mTestedSm.start();
        // Starting the state machine always puts us in a consistent state and notifies
        // the rest of the world that we've changed from an unknown to available state.
        mLooper.dispatchAll();
        reset(mNMService, mStatsService, mTetherHelper);
        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
        when(mTetheringDependencies.makeDhcpServer(
                any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
        when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
        when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);

        when(mRaDaemon.start()).thenReturn(true);
    }

    private void initTetheredStateMachine(int interfaceType, String upstreamIface)
            throws Exception {
        initTetheredStateMachine(interfaceType, upstreamIface, false);
    }

    private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception {
        initStateMachine(interfaceType);
    private void initTetheredStateMachine(int interfaceType, String upstreamIface,
            boolean usingLegacyDhcp) throws Exception {
        initStateMachine(interfaceType, usingLegacyDhcp);
        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
        if (upstreamIface != null) {
            dispatchTetherConnectionChanged(upstreamIface);
@@ -112,7 +147,7 @@ public class TetherInterfaceStateMachineTest {
    public void startsOutAvailable() {
        mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
                mTetheringDependencies);
                false /* usingLegacyDhcp */, mTetheringDependencies);
        mTestedSm.start();
        mLooper.dispatchAll();
        verify(mTetherHelper).updateInterfaceState(
@@ -345,6 +380,45 @@ public class TetherInterfaceStateMachineTest {
        }
    }

    @Test
    public void startsDhcpServer() throws Exception {
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
        dispatchTetherConnectionChanged(UPSTREAM_IFACE);

        assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
    }

    @Test
    public void startsDhcpServerOnBluetooth() throws Exception {
        initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
        dispatchTetherConnectionChanged(UPSTREAM_IFACE);

        assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
    }

    @Test
    public void doesNotStartDhcpServerIfDisabled() throws Exception {
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
        dispatchTetherConnectionChanged(UPSTREAM_IFACE);

        verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any());
    }

    private void assertDhcpStarted(IpPrefix expectedPrefix) {
        verify(mTetheringDependencies, times(1)).makeDhcpServer(
                eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
        verify(mDhcpServer, times(1)).start();
        final DhcpServingParams params = mDhcpParamsCaptor.getValue();
        // Last address byte is random
        assertTrue(expectedPrefix.contains(params.serverAddr.getAddress()));
        assertEquals(expectedPrefix.getPrefixLength(), params.serverAddr.getPrefixLength());
        assertEquals(1, params.defaultRouters.size());
        assertEquals(params.serverAddr.getAddress(), params.defaultRouters.iterator().next());
        assertEquals(1, params.dnsServers.size());
        assertEquals(params.serverAddr.getAddress(), params.dnsServers.iterator().next());
        assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
    }

    /**
     * Send a command to the state machine under test, and run the event loop to idle.
     *
Loading