Loading services/core/java/com/android/server/connectivity/Tethering.java +10 −4 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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); Loading Loading @@ -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(); } Loading services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +70 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +20 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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", Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); } Loading services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +8 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +78 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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( Loading Loading @@ -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 Loading
services/core/java/com/android/server/connectivity/Tethering.java +10 −4 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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); Loading Loading @@ -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(); } Loading
services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +70 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading
services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +20 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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", Loading @@ -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; Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); } Loading
services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +8 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } }
tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +78 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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( Loading Loading @@ -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