Tethering: ensure downstream prefix do not conflict with upstream
- Add New class PrivateAddressCoordinator to coordinate the private
address conflict problem.
- Downstream prefix would be random in 192.168.0.0/24 ~
192.168.255.0/24.
- If new upstream prefix is conflict with existing downstream prefix,
downstream would be kicked out and it would request a new one.
- The last conflict upstream prefixes would be blacklist. Avoid to
select downstream prefix which is conflict with prefixes in blacklist.
Bug: 130879722
Test: -build, flash, boot
-atest TetheringTests
Change-Id: Ib45b87bcd9eeb5da03fb7ec90b1af9ca53998cf5
This commit is contained in:
@@ -16,14 +16,13 @@
|
|||||||
|
|
||||||
package android.net.ip;
|
package android.net.ip;
|
||||||
|
|
||||||
import static android.net.InetAddresses.parseNumericAddress;
|
|
||||||
import static android.net.RouteInfo.RTN_UNICAST;
|
import static android.net.RouteInfo.RTN_UNICAST;
|
||||||
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
|
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
|
||||||
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
|
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
|
||||||
import static android.net.util.NetworkConstants.FF;
|
|
||||||
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
|
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
|
||||||
import static android.net.util.NetworkConstants.asByte;
|
import static android.net.util.NetworkConstants.asByte;
|
||||||
|
import static android.net.util.PrefixUtils.asIpPrefix;
|
||||||
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
|
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
|
||||||
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
|
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
|
||||||
|
|
||||||
@@ -66,11 +65,11 @@ import androidx.annotation.Nullable;
|
|||||||
import com.android.internal.util.MessageUtils;
|
import com.android.internal.util.MessageUtils;
|
||||||
import com.android.internal.util.State;
|
import com.android.internal.util.State;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
|
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -108,27 +107,8 @@ public class IpServer extends StateMachine {
|
|||||||
|
|
||||||
private static final byte DOUG_ADAMS = (byte) 42;
|
private static final byte DOUG_ADAMS = (byte) 42;
|
||||||
|
|
||||||
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
|
|
||||||
private static final int USB_PREFIX_LENGTH = 24;
|
|
||||||
private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
|
|
||||||
private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
|
|
||||||
private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
|
|
||||||
private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
|
|
||||||
private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
|
|
||||||
private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;
|
|
||||||
|
|
||||||
// TODO: remove this constant after introducing PrivateAddressCoordinator.
|
|
||||||
private static final List<IpPrefix> NCM_PREFIXES = Collections.unmodifiableList(
|
|
||||||
Arrays.asList(
|
|
||||||
new IpPrefix("192.168.42.0/24"),
|
|
||||||
new IpPrefix("192.168.51.0/24"),
|
|
||||||
new IpPrefix("192.168.52.0/24"),
|
|
||||||
new IpPrefix("192.168.53.0/24")
|
|
||||||
));
|
|
||||||
|
|
||||||
// TODO: have PanService use some visible version of this constant
|
// TODO: have PanService use some visible version of this constant
|
||||||
private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
|
private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
|
||||||
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
|
|
||||||
|
|
||||||
// TODO: have this configurable
|
// TODO: have this configurable
|
||||||
private static final int DHCP_LEASE_TIME_SECS = 3600;
|
private static final int DHCP_LEASE_TIME_SECS = 3600;
|
||||||
@@ -167,6 +147,14 @@ public class IpServer extends StateMachine {
|
|||||||
* Notify that the DHCP leases changed in one of the IpServers.
|
* Notify that the DHCP leases changed in one of the IpServers.
|
||||||
*/
|
*/
|
||||||
public void dhcpLeasesChanged() { }
|
public void dhcpLeasesChanged() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request Tethering change.
|
||||||
|
*
|
||||||
|
* @param tetheringType the downstream type of this IpServer.
|
||||||
|
* @param enabled enable or disable tethering.
|
||||||
|
*/
|
||||||
|
public void requestEnableTethering(int tetheringType, boolean enabled) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Capture IpServer dependencies, for injection. */
|
/** Capture IpServer dependencies, for injection. */
|
||||||
@@ -196,6 +184,7 @@ public class IpServer extends StateMachine {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a DhcpServer instance to be used by IpServer. */
|
/** Create a DhcpServer instance to be used by IpServer. */
|
||||||
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
||||||
DhcpServerCallbacks cb);
|
DhcpServerCallbacks cb);
|
||||||
@@ -225,16 +214,20 @@ public class IpServer extends StateMachine {
|
|||||||
public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
|
public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
|
||||||
// request from DHCP server that it wants to have a new prefix
|
// request from DHCP server that it wants to have a new prefix
|
||||||
public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
|
public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
|
||||||
|
// request from PrivateAddressCoordinator to restart tethering.
|
||||||
|
public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13;
|
||||||
|
|
||||||
private final State mInitialState;
|
private final State mInitialState;
|
||||||
private final State mLocalHotspotState;
|
private final State mLocalHotspotState;
|
||||||
private final State mTetheredState;
|
private final State mTetheredState;
|
||||||
private final State mUnavailableState;
|
private final State mUnavailableState;
|
||||||
|
private final State mWaitingForRestartState;
|
||||||
|
|
||||||
private final SharedLog mLog;
|
private final SharedLog mLog;
|
||||||
private final INetd mNetd;
|
private final INetd mNetd;
|
||||||
private final Callback mCallback;
|
private final Callback mCallback;
|
||||||
private final InterfaceController mInterfaceCtrl;
|
private final InterfaceController mInterfaceCtrl;
|
||||||
|
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
|
||||||
|
|
||||||
private final String mIfaceName;
|
private final String mIfaceName;
|
||||||
private final int mInterfaceType;
|
private final int mInterfaceType;
|
||||||
@@ -261,7 +254,6 @@ public class IpServer extends StateMachine {
|
|||||||
private int mDhcpServerStartIndex = 0;
|
private int mDhcpServerStartIndex = 0;
|
||||||
private IDhcpServer mDhcpServer;
|
private IDhcpServer mDhcpServer;
|
||||||
private RaParams mLastRaParams;
|
private RaParams mLastRaParams;
|
||||||
private LinkAddress mIpv4Address;
|
|
||||||
|
|
||||||
private LinkAddress mStaticIpv4ServerAddr;
|
private LinkAddress mStaticIpv4ServerAddr;
|
||||||
private LinkAddress mStaticIpv4ClientAddr;
|
private LinkAddress mStaticIpv4ClientAddr;
|
||||||
@@ -316,12 +308,14 @@ public class IpServer extends StateMachine {
|
|||||||
|
|
||||||
private final IpNeighborMonitor mIpNeighborMonitor;
|
private final IpNeighborMonitor mIpNeighborMonitor;
|
||||||
|
|
||||||
|
private LinkAddress mIpv4Address;
|
||||||
|
|
||||||
// TODO: Add a dependency object to pass the data members or variables from the tethering
|
// TODO: Add a dependency object to pass the data members or variables from the tethering
|
||||||
// object. It helps to reduce the arguments of the constructor.
|
// object. It helps to reduce the arguments of the constructor.
|
||||||
public IpServer(
|
public IpServer(
|
||||||
String ifaceName, Looper looper, int interfaceType, SharedLog log,
|
String ifaceName, Looper looper, int interfaceType, SharedLog log,
|
||||||
INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
|
INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
|
||||||
Dependencies deps) {
|
PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
|
||||||
super(ifaceName, looper);
|
super(ifaceName, looper);
|
||||||
mLog = log.forSubComponent(ifaceName);
|
mLog = log.forSubComponent(ifaceName);
|
||||||
mNetd = netd;
|
mNetd = netd;
|
||||||
@@ -332,6 +326,7 @@ public class IpServer extends StateMachine {
|
|||||||
mLinkProperties = new LinkProperties();
|
mLinkProperties = new LinkProperties();
|
||||||
mUsingLegacyDhcp = usingLegacyDhcp;
|
mUsingLegacyDhcp = usingLegacyDhcp;
|
||||||
mUsingBpfOffload = usingBpfOffload;
|
mUsingBpfOffload = usingBpfOffload;
|
||||||
|
mPrivateAddressCoordinator = addressCoordinator;
|
||||||
mDeps = deps;
|
mDeps = deps;
|
||||||
resetLinkProperties();
|
resetLinkProperties();
|
||||||
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
|
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
@@ -352,9 +347,11 @@ public class IpServer extends StateMachine {
|
|||||||
mLocalHotspotState = new LocalHotspotState();
|
mLocalHotspotState = new LocalHotspotState();
|
||||||
mTetheredState = new TetheredState();
|
mTetheredState = new TetheredState();
|
||||||
mUnavailableState = new UnavailableState();
|
mUnavailableState = new UnavailableState();
|
||||||
|
mWaitingForRestartState = new WaitingForRestartState();
|
||||||
addState(mInitialState);
|
addState(mInitialState);
|
||||||
addState(mLocalHotspotState);
|
addState(mLocalHotspotState);
|
||||||
addState(mTetheredState);
|
addState(mTetheredState);
|
||||||
|
addState(mWaitingForRestartState, mTetheredState);
|
||||||
addState(mUnavailableState);
|
addState(mUnavailableState);
|
||||||
|
|
||||||
setInitialState(mInitialState);
|
setInitialState(mInitialState);
|
||||||
@@ -387,6 +384,11 @@ public class IpServer extends StateMachine {
|
|||||||
return new LinkProperties(mLinkProperties);
|
return new LinkProperties(mLinkProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The address which IpServer is using. */
|
||||||
|
public LinkAddress getAddress() {
|
||||||
|
return mIpv4Address;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
|
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
|
||||||
* thread.
|
* thread.
|
||||||
@@ -617,6 +619,7 @@ public class IpServer extends StateMachine {
|
|||||||
// NOTE: All of configureIPv4() will be refactored out of existence
|
// NOTE: All of configureIPv4() will be refactored out of existence
|
||||||
// into calls to InterfaceController, shared with startIPv4().
|
// into calls to InterfaceController, shared with startIPv4().
|
||||||
mInterfaceCtrl.clearIPv4Address();
|
mInterfaceCtrl.clearIPv4Address();
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(this);
|
||||||
mIpv4Address = null;
|
mIpv4Address = null;
|
||||||
mStaticIpv4ServerAddr = null;
|
mStaticIpv4ServerAddr = null;
|
||||||
mStaticIpv4ClientAddr = null;
|
mStaticIpv4ClientAddr = null;
|
||||||
@@ -625,43 +628,24 @@ public class IpServer extends StateMachine {
|
|||||||
private boolean configureIPv4(boolean enabled) {
|
private boolean configureIPv4(boolean enabled) {
|
||||||
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
|
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
|
||||||
|
|
||||||
// TODO: Replace this hard-coded information with dynamically selected
|
if (enabled) {
|
||||||
// config passed down to us by a higher layer IP-coordinating element.
|
mIpv4Address = requestIpv4Address();
|
||||||
final Inet4Address srvAddr;
|
}
|
||||||
int prefixLen = 0;
|
|
||||||
try {
|
if (mIpv4Address == null) {
|
||||||
if (mStaticIpv4ServerAddr != null) {
|
mLog.e("No available ipv4 address");
|
||||||
srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
|
|
||||||
prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
|
|
||||||
} else if (mInterfaceType == TetheringManager.TETHERING_USB
|
|
||||||
|| mInterfaceType == TetheringManager.TETHERING_NCM) {
|
|
||||||
srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
|
|
||||||
prefixLen = USB_PREFIX_LENGTH;
|
|
||||||
} else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
|
|
||||||
srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address());
|
|
||||||
prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
|
|
||||||
} else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
|
|
||||||
srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
|
|
||||||
prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
|
|
||||||
} else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) {
|
|
||||||
// TODO: randomize address for tethering too, similarly to wifi
|
|
||||||
srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR);
|
|
||||||
prefixLen = ETHERNET_IFACE_PREFIX_LENGTH;
|
|
||||||
} else {
|
|
||||||
// BT configures the interface elsewhere: only start DHCP.
|
|
||||||
// TODO: make all tethering types behave the same way, and delete the bluetooth
|
|
||||||
// code that calls into NetworkManagementService directly.
|
|
||||||
srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
|
|
||||||
mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
|
|
||||||
return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
|
|
||||||
}
|
|
||||||
mIpv4Address = new LinkAddress(srvAddr, prefixLen);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
mLog.e("Error selecting ipv4 address", e);
|
|
||||||
if (!enabled) stopDhcp();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
|
||||||
|
// BT configures the interface elsewhere: only start DHCP.
|
||||||
|
// TODO: make all tethering types behave the same way, and delete the bluetooth
|
||||||
|
// code that calls into NetworkManagementService directly.
|
||||||
|
return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
|
||||||
|
}
|
||||||
|
|
||||||
|
final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
|
||||||
|
|
||||||
final Boolean setIfaceUp;
|
final Boolean setIfaceUp;
|
||||||
if (mInterfaceType == TetheringManager.TETHERING_WIFI
|
if (mInterfaceType == TetheringManager.TETHERING_WIFI
|
||||||
|| mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
|
|| mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
|
||||||
@@ -688,21 +672,14 @@ public class IpServer extends StateMachine {
|
|||||||
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) {
|
private LinkAddress requestIpv4Address() {
|
||||||
final byte[] ipv4Addr = rawAddr;
|
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
|
||||||
ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
|
|
||||||
try {
|
|
||||||
return (Inet4Address) InetAddress.getByAddress(ipv4Addr);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
mLog.e("Failed to construct Inet4Address from raw IPv4 addr");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRandomWifiIPv4Address() {
|
if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
|
||||||
final Inet4Address ipv4Addr =
|
return new LinkAddress(BLUETOOTH_IFACE_ADDR);
|
||||||
getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress());
|
}
|
||||||
return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR;
|
|
||||||
|
return mPrivateAddressCoordinator.requestDownstreamAddress(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startIPv6() {
|
private boolean startIPv6() {
|
||||||
@@ -978,19 +955,6 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: call PrivateAddressCoordinator.requestDownstreamAddress instead of this temporary
|
|
||||||
// logic.
|
|
||||||
private Inet4Address requestDownstreamAddress(@NonNull final IpPrefix currentPrefix) {
|
|
||||||
final int oldIndex = NCM_PREFIXES.indexOf(currentPrefix);
|
|
||||||
if (oldIndex == -1) {
|
|
||||||
mLog.e("current prefix isn't supported for NCM link: " + currentPrefix);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IpPrefix newPrefix = NCM_PREFIXES.get((oldIndex + 1) % NCM_PREFIXES.size());
|
|
||||||
return getRandomIPv4Address(newPrefix.getRawAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
|
private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
|
||||||
if (!currentPrefix.contains(mIpv4Address.getAddress())
|
if (!currentPrefix.contains(mIpv4Address.getAddress())
|
||||||
|| currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
|
|| currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
|
||||||
@@ -999,12 +963,12 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final LinkAddress deprecatedLinkAddress = mIpv4Address;
|
final LinkAddress deprecatedLinkAddress = mIpv4Address;
|
||||||
final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix);
|
mIpv4Address = requestIpv4Address();
|
||||||
if (srvAddr == null) {
|
if (mIpv4Address == null) {
|
||||||
mLog.e("Fail to request a new downstream prefix");
|
mLog.e("Fail to request a new downstream prefix");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength());
|
final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
|
||||||
|
|
||||||
// Add new IPv4 address on the interface.
|
// Add new IPv4 address on the interface.
|
||||||
if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
|
if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
|
||||||
@@ -1162,7 +1126,7 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address));
|
NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
|
||||||
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
|
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
|
||||||
mLog.e("Error Tethering", e);
|
mLog.e("Error Tethering", e);
|
||||||
mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
@@ -1222,6 +1186,11 @@ public class IpServer extends StateMachine {
|
|||||||
case CMD_NEW_PREFIX_REQUEST:
|
case CMD_NEW_PREFIX_REQUEST:
|
||||||
handleNewPrefixRequest((IpPrefix) message.obj);
|
handleNewPrefixRequest((IpPrefix) message.obj);
|
||||||
break;
|
break;
|
||||||
|
case CMD_NOTIFY_PREFIX_CONFLICT:
|
||||||
|
mLog.i("restart tethering: " + mInterfaceType);
|
||||||
|
mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
|
||||||
|
transitionTo(mWaitingForRestartState);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1403,6 +1372,28 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WaitingForRestartState extends State {
|
||||||
|
@Override
|
||||||
|
public boolean processMessage(Message message) {
|
||||||
|
logMessage(this, message.what);
|
||||||
|
switch (message.what) {
|
||||||
|
case CMD_TETHER_UNREQUESTED:
|
||||||
|
transitionTo(mInitialState);
|
||||||
|
mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
|
||||||
|
mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
|
||||||
|
break;
|
||||||
|
case CMD_INTERFACE_DOWN:
|
||||||
|
transitionTo(mUnavailableState);
|
||||||
|
mLog.i("Untethered (interface down) and restarting" + mIfaceName);
|
||||||
|
mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Accumulate routes representing "prefixes to be assigned to the local
|
// Accumulate routes representing "prefixes to be assigned to the local
|
||||||
// interface", for subsequent modification of local_network routing.
|
// interface", for subsequent modification of local_network routing.
|
||||||
private static ArrayList<RouteInfo> getLocalRoutesFor(
|
private static ArrayList<RouteInfo> getLocalRoutesFor(
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.networkstack.tethering;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.IpPrefix;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.ip.IpServer;
|
||||||
|
import android.net.util.PrefixUtils;
|
||||||
|
import android.util.ArrayMap;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
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.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class coordinate IP addresses conflict problem.
|
||||||
|
*
|
||||||
|
* Tethering downstream IP addresses may conflict with network assigned addresses. This
|
||||||
|
* coordinator is responsible for recording all of network assigned addresses and dispatched
|
||||||
|
* free address to downstream interfaces.
|
||||||
|
*
|
||||||
|
* This class is not thread-safe and should be accessed on the same tethering internal thread.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class PrivateAddressCoordinator {
|
||||||
|
public static final int PREFIX_LENGTH = 24;
|
||||||
|
|
||||||
|
private static final int MAX_UBYTE = 256;
|
||||||
|
private static final int BYTE_MASK = 0xff;
|
||||||
|
// reserved for bluetooth tethering.
|
||||||
|
private static final int BLUETOOTH_RESERVED = 44;
|
||||||
|
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().
|
||||||
|
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 final IpPrefix mTetheringPrefix;
|
||||||
|
private final ConnectivityManager mConnectivityMgr;
|
||||||
|
|
||||||
|
public PrivateAddressCoordinator(Context context) {
|
||||||
|
mDownstreams = new ArraySet<>();
|
||||||
|
mUpstreamPrefixMap = new ArrayMap<>();
|
||||||
|
mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
|
||||||
|
mConnectivityMgr = (ConnectivityManager) context.getSystemService(
|
||||||
|
Context.CONNECTIVITY_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a new upstream IpPrefix which may conflict with tethering downstreams.
|
||||||
|
* The downstreams will be notified if a conflict is found.
|
||||||
|
*/
|
||||||
|
public void updateUpstreamPrefix(final Network network, final LinkProperties lp) {
|
||||||
|
final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses());
|
||||||
|
if (ipv4Prefixes.isEmpty()) {
|
||||||
|
removeUpstreamPrefix(network);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mUpstreamPrefixMap.put(network, ipv4Prefixes);
|
||||||
|
handleMaybePrefixConflict(ipv4Prefixes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
|
||||||
|
final ArrayList<IpPrefix> list = new ArrayList<>();
|
||||||
|
for (LinkAddress address : linkAddresses) {
|
||||||
|
if (!address.isIpv4()) continue;
|
||||||
|
|
||||||
|
list.add(PrefixUtils.asIpPrefix(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
|
||||||
|
for (IpServer downstream : mDownstreams) {
|
||||||
|
final IpPrefix target = getDownstreamPrefix(downstream);
|
||||||
|
if (target == null) continue;
|
||||||
|
|
||||||
|
for (IpPrefix source : prefixes) {
|
||||||
|
if (isConflictPrefix(source, target)) {
|
||||||
|
downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove IpPrefix records corresponding to input network. */
|
||||||
|
public void removeUpstreamPrefix(final Network network) {
|
||||||
|
mUpstreamPrefixMap.remove(network);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeRemoveDeprectedUpstreams() {
|
||||||
|
if (!mDownstreams.isEmpty() || 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
mUpstreamPrefixMap.removeAll(toBeRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
// Address would be 192.168.[subAddress]/24.
|
||||||
|
final byte[] bytes = mTetheringPrefix.getRawAddress();
|
||||||
|
final int subAddress = getRandomSubAddr();
|
||||||
|
final int subNet = (subAddress >> 8) & BYTE_MASK;
|
||||||
|
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;
|
||||||
|
|
||||||
|
bytes[2] = (byte) newSubNet;
|
||||||
|
final InetAddress addr;
|
||||||
|
try {
|
||||||
|
addr = InetAddress.getByAddress(bytes);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new IllegalStateException("Invalid address, shouldn't happen.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
|
||||||
|
// Check whether this prefix is in use.
|
||||||
|
if (isDownstreamPrefixInUse(prefix)) continue;
|
||||||
|
// Check whether this prefix is conflict with any current upstream network.
|
||||||
|
if (isConflictWithUpstream(prefix)) continue;
|
||||||
|
|
||||||
|
mDownstreams.add(ipServer);
|
||||||
|
return new LinkAddress(addr, PREFIX_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No available address.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get random sub address value. Return value is in 0 ~ 0xffff. */
|
||||||
|
@VisibleForTesting
|
||||||
|
public int getRandomSubAddr() {
|
||||||
|
return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
|
||||||
|
final byte subId = (byte) (source & BYTE_MASK);
|
||||||
|
for (byte value : excluded) {
|
||||||
|
if (subId == value) return DEFAULT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release downstream record for IpServer. */
|
||||||
|
public void releaseDownstream(final IpServer ipServer) {
|
||||||
|
mDownstreams.remove(ipServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear current upstream prefixes records. */
|
||||||
|
public void clearUpstreamPrefixes() {
|
||||||
|
mUpstreamPrefixMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConflictWithUpstream(final IpPrefix source) {
|
||||||
|
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
|
||||||
|
final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
|
||||||
|
for (IpPrefix target : list) {
|
||||||
|
if (isConflictPrefix(source, target)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
|
||||||
|
if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
|
||||||
|
return prefix2.contains(prefix1.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix1.contains(prefix2.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDownstreamPrefixInUse(final IpPrefix source) {
|
||||||
|
// This class always generates downstream prefixes with the same prefix length, so
|
||||||
|
// prefixes cannot be contained in each other. They can only be equal to each other.
|
||||||
|
for (IpServer downstream : mDownstreams) {
|
||||||
|
final IpPrefix prefix = getDownstreamPrefix(downstream);
|
||||||
|
if (source.equals(prefix)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IpPrefix getDownstreamPrefix(final IpServer downstream) {
|
||||||
|
final LinkAddress address = downstream.getAddress();
|
||||||
|
if (address == null) return null;
|
||||||
|
|
||||||
|
return PrefixUtils.asIpPrefix(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(final IndentingPrintWriter pw) {
|
||||||
|
pw.decreaseIndent();
|
||||||
|
pw.println("mUpstreamPrefixMap:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
|
||||||
|
pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
pw.println("mDownstreams:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
for (IpServer ipServer : mDownstreams) {
|
||||||
|
pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
|
||||||
|
}
|
||||||
|
pw.decreaseIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -207,6 +207,7 @@ public class Tethering {
|
|||||||
new SparseArray<>();
|
new SparseArray<>();
|
||||||
|
|
||||||
// used to synchronize public access to members
|
// used to synchronize public access to members
|
||||||
|
// TODO(b/153621704): remove mPublicSync to make Tethering lock free
|
||||||
private final Object mPublicSync;
|
private final Object mPublicSync;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ArrayMap<String, TetherState> mTetherStates;
|
private final ArrayMap<String, TetherState> mTetherStates;
|
||||||
@@ -231,6 +232,7 @@ public class Tethering {
|
|||||||
private final TetheringThreadExecutor mExecutor;
|
private final TetheringThreadExecutor mExecutor;
|
||||||
private final TetheringNotificationUpdater mNotificationUpdater;
|
private final TetheringNotificationUpdater mNotificationUpdater;
|
||||||
private final UserManager mUserManager;
|
private final UserManager mUserManager;
|
||||||
|
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
|
||||||
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
|
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
|
||||||
// All the usage of mTetheringEventCallback should run in the same thread.
|
// All the usage of mTetheringEventCallback should run in the same thread.
|
||||||
private ITetheringEventCallback mTetheringEventCallback = null;
|
private ITetheringEventCallback mTetheringEventCallback = null;
|
||||||
@@ -314,6 +316,7 @@ public class Tethering {
|
|||||||
mExecutor = new TetheringThreadExecutor(mHandler);
|
mExecutor = new TetheringThreadExecutor(mHandler);
|
||||||
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
|
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
|
||||||
mNetdCallback = new NetdCallback();
|
mNetdCallback = new NetdCallback();
|
||||||
|
mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
|
||||||
|
|
||||||
// Load tethering configuration.
|
// Load tethering configuration.
|
||||||
updateConfiguration();
|
updateConfiguration();
|
||||||
@@ -1616,6 +1619,14 @@ public class Tethering {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addUpstreamPrefixes(final UpstreamNetworkState ns) {
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeUpstreamPrefixes(final UpstreamNetworkState ns) {
|
||||||
|
mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
|
void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
|
||||||
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
|
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
|
||||||
@@ -1624,6 +1635,14 @@ public class Tethering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final UpstreamNetworkState ns = (UpstreamNetworkState) o;
|
final UpstreamNetworkState ns = (UpstreamNetworkState) o;
|
||||||
|
switch (arg1) {
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
|
||||||
|
addUpstreamPrefixes(ns);
|
||||||
|
break;
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_LOST:
|
||||||
|
removeUpstreamPrefixes(ns);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ns == null || !pertainsToCurrentUpstream(ns)) {
|
if (ns == null || !pertainsToCurrentUpstream(ns)) {
|
||||||
// TODO: In future, this is where upstream evaluation and selection
|
// TODO: In future, this is where upstream evaluation and selection
|
||||||
@@ -2190,6 +2209,11 @@ public class Tethering {
|
|||||||
mOffloadController.dump(pw);
|
mOffloadController.dump(pw);
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("Private address coordinator:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
mPrivateAddressCoordinator.dump(pw);
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
pw.println("Log:");
|
pw.println("Log:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
if (argsContain(args, "--short")) {
|
if (argsContain(args, "--short")) {
|
||||||
@@ -2231,6 +2255,11 @@ public class Tethering {
|
|||||||
public void dhcpLeasesChanged() {
|
public void dhcpLeasesChanged() {
|
||||||
updateConnectedClients(null /* wifiClients */);
|
updateConnectedClients(null /* wifiClients */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestEnableTethering(int tetheringType, boolean enabled) {
|
||||||
|
enableTetheringInternal(tetheringType, enabled, null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2314,7 +2343,8 @@ public class Tethering {
|
|||||||
final TetherState tetherState = new TetherState(
|
final TetherState tetherState = new TetherState(
|
||||||
new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
|
new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
|
||||||
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
||||||
mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
|
mConfig.enableBpfOffload, mPrivateAddressCoordinator,
|
||||||
|
mDeps.getIpServerDependencies()));
|
||||||
mTetherStates.put(iface, tetherState);
|
mTetherStates.put(iface, tetherState);
|
||||||
tetherState.ipServer.start();
|
tetherState.ipServer.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
|
|||||||
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
||||||
import android.net.util.InterfaceParams;
|
import android.net.util.InterfaceParams;
|
||||||
import android.net.util.InterfaceSet;
|
import android.net.util.InterfaceSet;
|
||||||
|
import android.net.util.PrefixUtils;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
@@ -86,6 +87,8 @@ import android.text.TextUtils;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.networkstack.tethering.PrivateAddressCoordinator;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -109,7 +112,7 @@ public class IpServerTest {
|
|||||||
private static final String UPSTREAM_IFACE2 = "upstream1";
|
private static final String UPSTREAM_IFACE2 = "upstream1";
|
||||||
private static final int UPSTREAM_IFINDEX = 101;
|
private static final int UPSTREAM_IFINDEX = 101;
|
||||||
private static final int UPSTREAM_IFINDEX2 = 102;
|
private static final int UPSTREAM_IFINDEX2 = 102;
|
||||||
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
|
private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
|
||||||
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
|
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
|
||||||
private static final int DHCP_LEASE_TIME_SECS = 3600;
|
private static final int DHCP_LEASE_TIME_SECS = 3600;
|
||||||
private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
|
private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
|
||||||
@@ -119,6 +122,9 @@ public class IpServerTest {
|
|||||||
|
|
||||||
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
|
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
|
private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24");
|
||||||
|
private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
|
||||||
|
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
@Mock private IpServer.Callback mCallback;
|
@Mock private IpServer.Callback mCallback;
|
||||||
@Mock private SharedLog mSharedLog;
|
@Mock private SharedLog mSharedLog;
|
||||||
@@ -126,6 +132,7 @@ public class IpServerTest {
|
|||||||
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
||||||
@Mock private IpNeighborMonitor mIpNeighborMonitor;
|
@Mock private IpNeighborMonitor mIpNeighborMonitor;
|
||||||
@Mock private IpServer.Dependencies mDependencies;
|
@Mock private IpServer.Dependencies mDependencies;
|
||||||
|
@Mock private PrivateAddressCoordinator mAddressCoordinator;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||||
|
|
||||||
@@ -173,7 +180,7 @@ public class IpServerTest {
|
|||||||
|
|
||||||
mIpServer = new IpServer(
|
mIpServer = new IpServer(
|
||||||
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
|
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
|
||||||
mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
|
mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
|
||||||
mIpServer.start();
|
mIpServer.start();
|
||||||
mNeighborEventConsumer = neighborCaptor.getValue();
|
mNeighborEventConsumer = neighborCaptor.getValue();
|
||||||
|
|
||||||
@@ -200,12 +207,14 @@ public class IpServerTest {
|
|||||||
lp.setInterfaceName(upstreamIface);
|
lp.setInterfaceName(upstreamIface);
|
||||||
dispatchTetherConnectionChanged(upstreamIface, lp, 0);
|
dispatchTetherConnectionChanged(upstreamIface, lp, 0);
|
||||||
}
|
}
|
||||||
reset(mNetd, mCallback);
|
reset(mNetd, mCallback, mAddressCoordinator);
|
||||||
|
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before public void setUp() throws Exception {
|
@Before public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
|
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
|
||||||
|
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -214,7 +223,7 @@ public class IpServerTest {
|
|||||||
.thenReturn(mIpNeighborMonitor);
|
.thenReturn(mIpNeighborMonitor);
|
||||||
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
|
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
|
||||||
mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
|
mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
|
||||||
mDependencies);
|
mAddressCoordinator, mDependencies);
|
||||||
mIpServer.start();
|
mIpServer.start();
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
verify(mCallback).updateInterfaceState(
|
verify(mCallback).updateInterfaceState(
|
||||||
@@ -277,16 +286,17 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
|
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
|
||||||
|
|
||||||
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||||
InOrder inOrder = inOrder(mNetd, mCallback);
|
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
||||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
||||||
|
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
eq(mIpServer), any(LinkProperties.class));
|
eq(mIpServer), any(LinkProperties.class));
|
||||||
verifyNoMoreInteractions(mNetd, mCallback);
|
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -294,7 +304,8 @@ public class IpServerTest {
|
|||||||
initStateMachine(TETHERING_USB);
|
initStateMachine(TETHERING_USB);
|
||||||
|
|
||||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
|
||||||
InOrder inOrder = inOrder(mCallback, mNetd);
|
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
||||||
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
||||||
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||||
@@ -306,7 +317,7 @@ public class IpServerTest {
|
|||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
eq(mIpServer), mLinkPropertiesCaptor.capture());
|
eq(mIpServer), mLinkPropertiesCaptor.capture());
|
||||||
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
|
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
|
||||||
verifyNoMoreInteractions(mNetd, mCallback);
|
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -314,7 +325,8 @@ public class IpServerTest {
|
|||||||
initStateMachine(TETHERING_WIFI_P2P);
|
initStateMachine(TETHERING_WIFI_P2P);
|
||||||
|
|
||||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
|
||||||
InOrder inOrder = inOrder(mCallback, mNetd);
|
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
||||||
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
||||||
IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
|
IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||||
@@ -326,7 +338,7 @@ public class IpServerTest {
|
|||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
eq(mIpServer), mLinkPropertiesCaptor.capture());
|
eq(mIpServer), mLinkPropertiesCaptor.capture());
|
||||||
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
|
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
|
||||||
verifyNoMoreInteractions(mNetd, mCallback);
|
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -392,18 +404,19 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
||||||
|
|
||||||
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||||
InOrder inOrder = inOrder(mNetd, mCallback);
|
InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
|
||||||
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
||||||
|
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
eq(mIpServer), any(LinkProperties.class));
|
eq(mIpServer), any(LinkProperties.class));
|
||||||
verifyNoMoreInteractions(mNetd, mCallback);
|
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -483,7 +496,7 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
|
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
|
assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -491,7 +504,7 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
|
assertDhcpStarted(mBluetoothPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -499,7 +512,7 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
|
initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
|
assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -524,21 +537,27 @@ public class IpServerTest {
|
|||||||
eventCallbacks = dhcpEventCbsCaptor.getValue();
|
eventCallbacks = dhcpEventCbsCaptor.getValue();
|
||||||
assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
|
assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
|
||||||
|
|
||||||
// Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
|
|
||||||
// onNewPrefixRequest callback.
|
|
||||||
eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
|
|
||||||
mLooper.dispatchAll();
|
|
||||||
|
|
||||||
final ArgumentCaptor<LinkProperties> lpCaptor =
|
final ArgumentCaptor<LinkProperties> lpCaptor =
|
||||||
ArgumentCaptor.forClass(LinkProperties.class);
|
ArgumentCaptor.forClass(LinkProperties.class);
|
||||||
InOrder inOrder = inOrder(mNetd, mCallback);
|
InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
|
||||||
mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
|
|
||||||
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
|
|
||||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
// One for ipv4 route, one for ipv6 link local route.
|
// One for ipv4 route, one for ipv6 link local route.
|
||||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
||||||
any(), any());
|
any(), any());
|
||||||
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
|
mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
|
||||||
|
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
|
||||||
|
verifyNoMoreInteractions(mCallback, mAddressCoordinator);
|
||||||
|
|
||||||
|
// Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
|
||||||
|
// onNewPrefixRequest callback.
|
||||||
|
final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
|
||||||
|
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress);
|
||||||
|
eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
|
||||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
|
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
|
||||||
verifyNoMoreInteractions(mCallback);
|
verifyNoMoreInteractions(mCallback);
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.networkstack.tethering;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.InetAddresses;
|
||||||
|
import android.net.IpPrefix;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.ip.IpServer;
|
||||||
|
import android.net.util.NetworkConstants;
|
||||||
|
import android.net.util.PrefixUtils;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public final class PrivateAddressCoordinatorTest {
|
||||||
|
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
||||||
|
private static final String TEST_WIFI_IFNAME = "test_wlan0";
|
||||||
|
|
||||||
|
@Mock private IpServer mHotspotIpServer;
|
||||||
|
@Mock private IpServer mUsbIpServer;
|
||||||
|
@Mock private IpServer mEthernetIpServer;
|
||||||
|
@Mock private Context mContext;
|
||||||
|
@Mock private ConnectivityManager mConnectivityMgr;
|
||||||
|
|
||||||
|
private PrivateAddressCoordinator mPrivateAddressCoordinator;
|
||||||
|
private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
|
||||||
|
private final Network mWifiNetwork = new Network(1);
|
||||||
|
private final Network mMobileNetwork = new Network(2);
|
||||||
|
private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
|
||||||
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDownstreamPrefixRequest() throws Exception {
|
||||||
|
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertNotEquals(hotspotPrefix, mBluetoothPrefix);
|
||||||
|
|
||||||
|
address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertNotEquals(hotspotPrefix, testDupRequest);
|
||||||
|
assertNotEquals(mBluetoothPrefix, testDupRequest);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
|
||||||
|
address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mUsbIpServer);
|
||||||
|
final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertNotEquals(usbPrefix, mBluetoothPrefix);
|
||||||
|
assertNotEquals(usbPrefix, hotspotPrefix);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRequestDownstreamAddress() throws Exception {
|
||||||
|
LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24");
|
||||||
|
int fakeSubAddr = 0x2b00;
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
|
||||||
|
LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
assertEquals(actualAddress, expectedAddress);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
|
||||||
|
fakeSubAddr = 0x2b01;
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
|
||||||
|
actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
assertEquals(actualAddress, expectedAddress);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
|
||||||
|
fakeSubAddr = 0x2bff;
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
|
||||||
|
actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
assertEquals(actualAddress, expectedAddress);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
|
||||||
|
expectedAddress = new LinkAddress("192.168.43.5/24");
|
||||||
|
fakeSubAddr = 0x2b05;
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
|
||||||
|
actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
assertEquals(actualAddress, expectedAddress);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReserveBluetoothPrefix() throws Exception {
|
||||||
|
final int fakeSubAddr = 0x2c05;
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
|
||||||
|
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoConflictDownstreamPrefix() throws Exception {
|
||||||
|
final int fakeHotspotSubAddr = 0x2b05;
|
||||||
|
final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
|
||||||
|
LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
|
||||||
|
when(mHotspotIpServer.getAddress()).thenReturn(address);
|
||||||
|
|
||||||
|
address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mUsbIpServer);
|
||||||
|
final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertNotEquals(predefinedPrefix, usbPrefix);
|
||||||
|
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
|
||||||
|
address = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mUsbIpServer);
|
||||||
|
final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
|
||||||
|
assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
|
||||||
|
boolean isMobile) {
|
||||||
|
final String testIface;
|
||||||
|
final String testIpv4Address;
|
||||||
|
if (isMobile) {
|
||||||
|
testIface = TEST_MOBILE_IFNAME;
|
||||||
|
testIpv4Address = "10.0.0.1";
|
||||||
|
} else {
|
||||||
|
testIface = TEST_WIFI_IFNAME;
|
||||||
|
testIpv4Address = "192.168.43.5";
|
||||||
|
}
|
||||||
|
|
||||||
|
final LinkProperties prop = new LinkProperties();
|
||||||
|
prop.setInterfaceName(testIface);
|
||||||
|
|
||||||
|
if (withIPv4) {
|
||||||
|
prop.addLinkAddress(
|
||||||
|
new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
|
||||||
|
NetworkConstants.IPV4_ADDR_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withIPv6) {
|
||||||
|
prop.addLinkAddress(
|
||||||
|
new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
|
||||||
|
NetworkConstants.RFC7421_PREFIX_LENGTH));
|
||||||
|
}
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoConflictUpstreamPrefix() throws Exception {
|
||||||
|
final int fakeHotspotSubId = 43;
|
||||||
|
final int fakeHotspotSubAddr = 0x2b05;
|
||||||
|
final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
|
||||||
|
// Force always get subAddress "43.5" for conflict testing.
|
||||||
|
when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
|
||||||
|
// 1. Enable hotspot with prefix 192.168.43.0/24
|
||||||
|
final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
|
||||||
|
assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
|
||||||
|
when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
|
||||||
|
// 2. Update v6 only mobile network, hotspot prefix should not be removed.
|
||||||
|
List<String> testConflicts;
|
||||||
|
final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
|
||||||
|
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
|
||||||
|
// 3. Update v4 only mobile network, hotspot prefix should not be removed.
|
||||||
|
final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
|
||||||
|
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
// 4. Update v4v6 mobile network, hotspot prefix should not be removed.
|
||||||
|
final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
|
||||||
|
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
// 5. Update v6 only wifi network, hotspot prefix should not be removed.
|
||||||
|
final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
|
||||||
|
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
|
||||||
|
// 6. Update v4 only wifi network, it conflict with hotspot prefix.
|
||||||
|
final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
|
||||||
|
verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
reset(mHotspotIpServer);
|
||||||
|
// 7. Restart hotspot again and its prefix is different previous.
|
||||||
|
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
|
||||||
|
final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mHotspotIpServer);
|
||||||
|
final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
|
||||||
|
assertNotEquals(hotspotPrefix, hotspotPrefix2);
|
||||||
|
when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
|
||||||
|
mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
|
||||||
|
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
|
||||||
|
// 7. Usb tethering can be enabled and its prefix is different with conflict one.
|
||||||
|
final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mUsbIpServer);
|
||||||
|
final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
|
||||||
|
assertNotEquals(predefinedPrefix, usbPrefix);
|
||||||
|
assertNotEquals(hotspotPrefix2, usbPrefix);
|
||||||
|
when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
|
||||||
|
// 8. Disable wifi upstream, then wifi's prefix can be selected again.
|
||||||
|
mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
|
||||||
|
final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
|
||||||
|
mEthernetIpServer);
|
||||||
|
final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
|
||||||
|
assertEquals(predefinedPrefix, ethPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import static android.net.TetheringManager.TETHERING_ETHERNET;
|
|||||||
import static android.net.TetheringManager.TETHERING_NCM;
|
import static android.net.TetheringManager.TETHERING_NCM;
|
||||||
import static android.net.TetheringManager.TETHERING_USB;
|
import static android.net.TetheringManager.TETHERING_USB;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
|
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||||
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
|
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
|
||||||
@@ -84,6 +85,7 @@ import android.content.res.Resources;
|
|||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.EthernetManager;
|
import android.net.EthernetManager;
|
||||||
|
import android.net.EthernetManager.TetheredInterfaceCallback;
|
||||||
import android.net.EthernetManager.TetheredInterfaceRequest;
|
import android.net.EthernetManager.TetheredInterfaceRequest;
|
||||||
import android.net.IIntResultListener;
|
import android.net.IIntResultListener;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
@@ -169,9 +171,11 @@ public class TetheringTest {
|
|||||||
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
||||||
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
|
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
|
||||||
private static final String TEST_USB_IFNAME = "test_rndis0";
|
private static final String TEST_USB_IFNAME = "test_rndis0";
|
||||||
private static final String TEST_WLAN_IFNAME = "test_wlan0";
|
private static final String TEST_WIFI_IFNAME = "test_wlan0";
|
||||||
|
private static final String TEST_WLAN_IFNAME = "test_wlan1";
|
||||||
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
|
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
|
||||||
private static final String TEST_NCM_IFNAME = "test_ncm0";
|
private static final String TEST_NCM_IFNAME = "test_ncm0";
|
||||||
|
private static final String TEST_ETH_IFNAME = "test_eth0";
|
||||||
private static final String TETHERING_NAME = "Tethering";
|
private static final String TETHERING_NAME = "Tethering";
|
||||||
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
|
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
|
||||||
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
|
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
|
||||||
@@ -279,10 +283,11 @@ public class TetheringTest {
|
|||||||
|| ifName.equals(TEST_WLAN_IFNAME)
|
|| ifName.equals(TEST_WLAN_IFNAME)
|
||||||
|| ifName.equals(TEST_MOBILE_IFNAME)
|
|| ifName.equals(TEST_MOBILE_IFNAME)
|
||||||
|| ifName.equals(TEST_P2P_IFNAME)
|
|| ifName.equals(TEST_P2P_IFNAME)
|
||||||
|| ifName.equals(TEST_NCM_IFNAME));
|
|| ifName.equals(TEST_NCM_IFNAME)
|
||||||
|
|| ifName.equals(TEST_ETH_IFNAME));
|
||||||
final String[] ifaces = new String[] {
|
final String[] ifaces = new String[] {
|
||||||
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
|
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
|
||||||
TEST_NCM_IFNAME};
|
TEST_NCM_IFNAME, TEST_ETH_IFNAME};
|
||||||
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
|
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
|
||||||
MacAddress.ALL_ZEROS_ADDRESS);
|
MacAddress.ALL_ZEROS_ADDRESS);
|
||||||
}
|
}
|
||||||
@@ -490,7 +495,7 @@ public class TetheringTest {
|
|||||||
when(mNetd.interfaceGetList())
|
when(mNetd.interfaceGetList())
|
||||||
.thenReturn(new String[] {
|
.thenReturn(new String[] {
|
||||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
|
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
|
||||||
TEST_NCM_IFNAME});
|
TEST_NCM_IFNAME, TEST_ETH_IFNAME});
|
||||||
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
|
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
|
||||||
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
||||||
mInterfaceConfiguration.flags = new String[0];
|
mInterfaceConfiguration.flags = new String[0];
|
||||||
@@ -1836,6 +1841,109 @@ public class TetheringTest {
|
|||||||
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
|
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
|
||||||
sendConfigurationChanged();
|
sendConfigurationChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
|
||||||
|
final int prefixLength, final Network network) {
|
||||||
|
final LinkProperties prop = new LinkProperties();
|
||||||
|
prop.setInterfaceName(TEST_WIFI_IFNAME);
|
||||||
|
|
||||||
|
prop.addLinkAddress(
|
||||||
|
new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
|
||||||
|
prefixLength));
|
||||||
|
|
||||||
|
final NetworkCapabilities capabilities = new NetworkCapabilities()
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
|
||||||
|
return new UpstreamNetworkState(prop, capabilities, network);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleIpConflict() throws Exception {
|
||||||
|
final Network wifiNetwork = new Network(200);
|
||||||
|
final Network[] allNetworks = { wifiNetwork };
|
||||||
|
when(mCm.getAllNetworks()).thenReturn(allNetworks);
|
||||||
|
UpstreamNetworkState upstreamNetwork = null;
|
||||||
|
runUsbTethering(upstreamNetwork);
|
||||||
|
final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
|
||||||
|
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
|
||||||
|
verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
|
||||||
|
final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr;
|
||||||
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||||
|
any(), any());
|
||||||
|
reset(mNetd, mUsbManager);
|
||||||
|
upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
|
||||||
|
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
|
||||||
|
Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
|
||||||
|
UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
|
||||||
|
0,
|
||||||
|
upstreamNetwork);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
// verify trun off usb tethering
|
||||||
|
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||||
|
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
// verify restart usb tethering
|
||||||
|
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoAddressAvailable() throws Exception {
|
||||||
|
final Network wifiNetwork = new Network(200);
|
||||||
|
final Network[] allNetworks = { wifiNetwork };
|
||||||
|
when(mCm.getAllNetworks()).thenReturn(allNetworks);
|
||||||
|
final String upstreamAddress = "192.168.0.100";
|
||||||
|
runUsbTethering(null);
|
||||||
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||||
|
any(), any());
|
||||||
|
reset(mUsbManager);
|
||||||
|
final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
|
||||||
|
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
|
||||||
|
final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
|
||||||
|
mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
|
||||||
|
TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
|
||||||
|
ethCallback.onAvailable(TEST_ETH_IFNAME);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
reset(mUsbManager, mEm);
|
||||||
|
|
||||||
|
final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
|
||||||
|
upstreamAddress, 16, wifiNetwork);
|
||||||
|
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
|
||||||
|
Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
|
||||||
|
UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
|
||||||
|
0,
|
||||||
|
upstreamNetwork);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
// verify trun off usb tethering
|
||||||
|
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||||
|
// verify trun off ethernet tethering
|
||||||
|
verify(mockRequest).release();
|
||||||
|
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||||
|
ethCallback.onUnavailable();
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
// verify restart usb tethering
|
||||||
|
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||||
|
// verify restart ethernet tethering
|
||||||
|
verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
|
||||||
|
ethCallback = callbackCaptor.getValue();
|
||||||
|
ethCallback.onAvailable(TEST_ETH_IFNAME);
|
||||||
|
|
||||||
|
reset(mUsbManager, mEm);
|
||||||
|
when(mNetd.interfaceGetList())
|
||||||
|
.thenReturn(new String[] {
|
||||||
|
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
|
||||||
|
TEST_NCM_IFNAME, TEST_ETH_IFNAME});
|
||||||
|
|
||||||
|
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
|
||||||
|
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME);
|
||||||
|
assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME);
|
||||||
|
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME));
|
||||||
|
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Test that a request for hotspot mode doesn't interfere with an
|
// TODO: Test that a request for hotspot mode doesn't interfere with an
|
||||||
// already operating tethering mode interface.
|
// already operating tethering mode interface.
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user