Merge changes If7997341,I7584253b into mainline-prod

* changes:
  Allow wifi p2p to use legacy dedicated address
  Address comment from aosp/1232197
This commit is contained in:
Mark Chien
2020-08-05 12:03:09 +00:00
committed by Android (Google) Code Review
8 changed files with 136 additions and 25 deletions

View File

@@ -73,6 +73,9 @@
<!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
<bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
<!-- Use legacy wifi p2p dedicated address instead of randomize address. -->
<bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool>
<!-- Dhcp range (min, max) to use for tethering purposes -->
<string-array translatable="false" name="config_tether_dhcp_range">
</string-array>

View File

@@ -30,6 +30,7 @@
-->
<item type="bool" name="config_tether_enable_bpf_offload"/>
<item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
<item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/>
<item type="integer" name="config_tether_offload_poll_interval"/>
<item type="array" name="config_tether_upstream_types"/>
<item type="bool" name="config_tether_upstream_automatic"/>

View File

@@ -15,6 +15,10 @@
*/
package com.android.networkstack.tethering;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static java.util.Arrays.asList;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.IpPrefix;
@@ -34,9 +38,10 @@ import com.android.internal.util.IndentingPrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
/**
* This class coordinate IP addresses conflict problem.
@@ -55,28 +60,32 @@ public class PrivateAddressCoordinator {
private static final int BYTE_MASK = 0xff;
// reserved for bluetooth tethering.
private static final int BLUETOOTH_RESERVED = 44;
private static final int WIFI_P2P_RESERVED = 49;
private static final byte DEFAULT_ID = (byte) 42;
// Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
// address may be requested before coordinator get current upstream notification. To ensure
// coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
// when tethering is down. Instead coordinator would remove all depcreted upstreams from
// mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams().
// when tethering is down. Instead tethering would remove all deprecated upstreams from
// mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
private final ArraySet<IpServer> mDownstreams;
// IANA has reserved the following three blocks of the IP address space for private intranets:
// 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
// Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
private final IpPrefix mTetheringPrefix;
private final ConnectivityManager mConnectivityMgr;
private final TetheringConfiguration mConfig;
public PrivateAddressCoordinator(Context context) {
public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
mDownstreams = new ArraySet<>();
mUpstreamPrefixMap = new ArrayMap<>();
mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
mConnectivityMgr = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
mConfig = config;
}
/**
@@ -124,26 +133,34 @@ public class PrivateAddressCoordinator {
mUpstreamPrefixMap.remove(network);
}
private void maybeRemoveDeprectedUpstreams() {
if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return;
/**
* Maybe remove deprecated upstream records, this would be called once tethering started without
* any exiting tethered downstream.
*/
public void maybeRemoveDeprecatedUpstreams() {
if (mUpstreamPrefixMap.isEmpty()) return;
final ArrayList<Network> toBeRemoved = new ArrayList<>();
List<Network> allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks());
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
final Network network = mUpstreamPrefixMap.keyAt(i);
if (!allNetworks.contains(network)) toBeRemoved.add(network);
}
// Remove all upstreams that are no longer valid networks
final Set<Network> toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet());
toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks()));
mUpstreamPrefixMap.removeAll(toBeRemoved);
}
private boolean isReservedSubnet(final int subnet) {
return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED;
}
/**
* Pick a random available address and mark its prefix as in use for the provided IpServer,
* returns null if there is no available address.
*/
@Nullable
public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
maybeRemoveDeprectedUpstreams();
if (mConfig.shouldEnableWifiP2pDedicatedIp()
&& ipServer.interfaceType() == TETHERING_WIFI_P2P) {
return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
}
// Address would be 192.168.[subAddress]/24.
final byte[] bytes = mTetheringPrefix.getRawAddress();
@@ -152,7 +169,7 @@ public class PrivateAddressCoordinator {
bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
for (int i = 0; i < MAX_UBYTE; i++) {
final int newSubNet = (subNet + i) & BYTE_MASK;
if (newSubNet == BLUETOOTH_RESERVED) continue;
if (isReservedSubnet(newSubNet)) continue;
bytes[2] = (byte) newSubNet;
final InetAddress addr;
@@ -237,7 +254,6 @@ public class PrivateAddressCoordinator {
}
void dump(final IndentingPrintWriter pw) {
pw.decreaseIndent();
pw.println("mUpstreamPrefixMap:");
pw.increaseIndent();
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {

View File

@@ -320,10 +320,13 @@ public class Tethering {
mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
mNetdCallback = new NetdCallback();
mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
// Load tethering configuration.
updateConfiguration();
// It is OK for the configuration to be passed to the PrivateAddressCoordinator at
// construction time because the only part of the configuration it uses is
// shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
// Must be initialized after tethering configuration is loaded because BpfCoordinator
// constructor needs to use the configuration.
@@ -1751,6 +1754,7 @@ public class Tethering {
return;
}
mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams();
mUpstreamNetworkMonitor.startObserveAllNetworks();
// TODO: De-duplicate with updateUpstreamWanted() below.

View File

@@ -84,6 +84,9 @@ public class TetheringConfiguration {
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
"tether_enable_legacy_dhcp_server";
public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
"use_legacy_wifi_p2p_dedicated_ip";
/**
* Default value that used to periodic polls tether offload stats from tethering offload HAL
* to make the data warnings work.
@@ -113,6 +116,7 @@ public class TetheringConfiguration {
private final int mOffloadPollInterval;
// TODO: Add to TetheringConfigurationParcel if required.
private final boolean mEnableBpfOffload;
private final boolean mEnableWifiP2pDedicatedIp;
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
@@ -156,6 +160,10 @@ public class TetheringConfiguration {
R.integer.config_tether_offload_poll_interval,
DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
false /* defaultValue */);
configLog.log(toString());
}
@@ -199,6 +207,11 @@ public class TetheringConfiguration {
return !TextUtils.isEmpty(provisioningAppNoUi);
}
/** Check whether dedicated wifi p2p address is enabled. */
public boolean shouldEnableWifiP2pDedicatedIp() {
return mEnableWifiP2pDedicatedIp;
}
/** Does the dumping.*/
public void dump(PrintWriter pw) {
pw.print("activeDataSubId: ");
@@ -233,6 +246,9 @@ public class TetheringConfiguration {
pw.print("enableLegacyDhcpServer: ");
pw.println(enableLegacyDhcpServer);
pw.print("enableWifiP2pDedicatedIp: ");
pw.println(mEnableWifiP2pDedicatedIp);
}
/** Returns the string representation of this object.*/

View File

@@ -15,6 +15,11 @@
*/
package com.android.networkstack.tethering;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.never;
@@ -54,22 +59,34 @@ public final class PrivateAddressCoordinatorTest {
@Mock private IpServer mHotspotIpServer;
@Mock private IpServer mUsbIpServer;
@Mock private IpServer mEthernetIpServer;
@Mock private IpServer mWifiP2pIpServer;
@Mock private Context mContext;
@Mock private ConnectivityManager mConnectivityMgr;
@Mock private TetheringConfiguration mConfig;
private PrivateAddressCoordinator mPrivateAddressCoordinator;
private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
private final Network mWifiNetwork = new Network(1);
private final Network mMobileNetwork = new Network(2);
private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
private void setUpIpServers() throws Exception {
when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
setUpIpServers();
mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
}
@Test
@@ -127,10 +144,15 @@ public final class PrivateAddressCoordinatorTest {
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
}
private int getBluetoothSubAddress() {
final byte[] rawAddress = mBluetoothPrefix.getRawAddress();
int bluetoothSubNet = rawAddress[2] & 0xff;
return (bluetoothSubNet << 8) + 0x5;
}
@Test
public void testReserveBluetoothPrefix() throws Exception {
final int fakeSubAddr = 0x2c05;
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(getBluetoothSubAddress());
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
mHotspotIpServer);
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
@@ -146,7 +168,7 @@ public final class PrivateAddressCoordinatorTest {
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
mHotspotIpServer);
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
when(mHotspotIpServer.getAddress()).thenReturn(address);
address = mPrivateAddressCoordinator.requestDownstreamAddress(
@@ -159,7 +181,7 @@ public final class PrivateAddressCoordinatorTest {
address = mPrivateAddressCoordinator.requestDownstreamAddress(
mUsbIpServer);
final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix);
assertEquals("Fail to reselect available prefix: ", predefinedPrefix, allowUseFreePrefix);
}
private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
@@ -202,7 +224,7 @@ public final class PrivateAddressCoordinatorTest {
final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
mHotspotIpServer);
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
// 2. Update v6 only mobile network, hotspot prefix should not be removed.
List<String> testConflicts;
@@ -251,4 +273,38 @@ public final class PrivateAddressCoordinatorTest {
final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
assertEquals(predefinedPrefix, ethPrefix);
}
private int getSubAddress(final byte... ipv4Address) {
assertEquals(4, ipv4Address.length);
int subnet = Byte.toUnsignedInt(ipv4Address[2]);
return (subnet << 8) + ipv4Address[3];
}
private void assertReseveredWifiP2pPrefix() throws Exception {
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
mHotspotIpServer);
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress);
assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
}
@Test
public void testEnableLegacyWifiP2PAddress() throws Exception {
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
// No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
// is resevered.
assertReseveredWifiP2pPrefix();
when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true);
assertReseveredWifiP2pPrefix();
// If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
mWifiP2pIpServer);
assertEquals(mLegacyWifiP2pAddress, address);
mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
}
}

View File

@@ -128,6 +128,8 @@ public class TetheringConfigurationTest {
.thenReturn(new String[0]);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
.thenReturn(false);
initializeBpfOffloadConfiguration(true, null /* unset */);
mHasTelephonyManager = true;
@@ -413,4 +415,17 @@ public class TetheringConfigurationTest {
R.string.config_mobile_hotspot_provision_response)).thenReturn(
PROVISIONING_APP_RESPONSE);
}
@Test
public void testEnableLegacyWifiP2PAddress() throws Exception {
final TetheringConfiguration defaultCfg = new TetheringConfiguration(
mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
.thenReturn(true);
final TetheringConfiguration testCfg = new TetheringConfiguration(
mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
}
}

View File

@@ -1897,7 +1897,7 @@ public class TetheringTest {
0,
upstreamNetwork);
mLooper.dispatchAll();
// verify trun off usb tethering
// verify turn off usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
mTethering.interfaceRemoved(TEST_USB_IFNAME);
mLooper.dispatchAll();
@@ -1935,9 +1935,9 @@ public class TetheringTest {
0,
upstreamNetwork);
mLooper.dispatchAll();
// verify trun off usb tethering
// verify turn off usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
// verify trun off ethernet tethering
// verify turn off ethernet tethering
verify(mockRequest).release();
mTethering.interfaceRemoved(TEST_USB_IFNAME);
ethCallback.onUnavailable();