Support MirrorLink DHCPDECLINE.
Add the specific implementation of onNewPrefixRequest callback on IpServer side, also refactor some common code. Bug: 130741856 Test: atest TetheringTests Change-Id: If2871bf899cb5890bbfee18063a194c92b6f474e
This commit is contained in:
@@ -173,6 +173,18 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether the DHCP server should request a new prefix from IpServer when receiving
|
||||||
|
* DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB
|
||||||
|
* tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests.
|
||||||
|
*
|
||||||
|
* <p>If not set, the default value is false.
|
||||||
|
*/
|
||||||
|
public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) {
|
||||||
|
this.changePrefixOnDecline = changePrefixOnDecline;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
|
private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
|
||||||
int[] res = new int[addrs.size()];
|
int[] res = new int[addrs.size()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import android.net.shared.NetdUtils;
|
|||||||
import android.net.shared.RouteUtils;
|
import android.net.shared.RouteUtils;
|
||||||
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.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -60,6 +61,7 @@ import android.util.Log;
|
|||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
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;
|
||||||
@@ -115,6 +117,15 @@ public class IpServer extends StateMachine {
|
|||||||
private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
|
private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
|
||||||
private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;
|
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";
|
||||||
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
|
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
|
||||||
@@ -212,6 +223,8 @@ public class IpServer extends StateMachine {
|
|||||||
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
|
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
|
||||||
// new neighbor cache entry on our interface
|
// new neighbor cache entry on our interface
|
||||||
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
|
||||||
|
public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
|
||||||
|
|
||||||
private final State mInitialState;
|
private final State mInitialState;
|
||||||
private final State mLocalHotspotState;
|
private final State mLocalHotspotState;
|
||||||
@@ -462,7 +475,7 @@ public class IpServer extends StateMachine {
|
|||||||
handleError();
|
handleError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new DhcpLeaseCallback());
|
}, new DhcpEventCallback());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
@@ -475,7 +488,7 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub {
|
private class DhcpEventCallback extends IDhcpEventCallbacks.Stub {
|
||||||
@Override
|
@Override
|
||||||
public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
|
public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
|
||||||
final ArrayList<TetheredClient> leases = new ArrayList<>();
|
final ArrayList<TetheredClient> leases = new ArrayList<>();
|
||||||
@@ -509,8 +522,9 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewPrefixRequest(IpPrefix currentPrefix) {
|
public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
|
||||||
//TODO: add specific implementation.
|
Objects.requireNonNull(currentPrefix);
|
||||||
|
sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -524,26 +538,38 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) {
|
||||||
|
Objects.requireNonNull(ipv4Address);
|
||||||
|
return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter,
|
||||||
|
@NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr,
|
||||||
|
@Nullable Inet4Address clientAddr) {
|
||||||
|
final boolean changePrefixOnDecline =
|
||||||
|
(mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null);
|
||||||
|
return new DhcpServingParamsParcelExt()
|
||||||
|
.setDefaultRouters(defaultRouter)
|
||||||
|
.setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
|
||||||
|
.setDnsServers(dnsServer)
|
||||||
|
.setServerAddr(serverAddr)
|
||||||
|
.setMetered(true)
|
||||||
|
.setSingleClientAddr(clientAddr)
|
||||||
|
.setChangePrefixOnDecline(changePrefixOnDecline);
|
||||||
|
// TODO: also advertise link MTU
|
||||||
|
}
|
||||||
|
|
||||||
private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
|
private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
|
||||||
if (mUsingLegacyDhcp) {
|
if (mUsingLegacyDhcp) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
|
final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
|
||||||
final int prefixLen = serverLinkAddr.getPrefixLength();
|
|
||||||
final Inet4Address clientAddr = clientLinkAddr == null ? null :
|
final Inet4Address clientAddr = clientLinkAddr == null ? null :
|
||||||
(Inet4Address) clientLinkAddr.getAddress();
|
(Inet4Address) clientLinkAddr.getAddress();
|
||||||
|
|
||||||
final DhcpServingParamsParcel params;
|
final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,
|
||||||
params = new DhcpServingParamsParcelExt()
|
addr /* dnsServer */, serverLinkAddr, clientAddr);
|
||||||
.setDefaultRouters(addr)
|
|
||||||
.setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
|
|
||||||
.setDnsServers(addr)
|
|
||||||
.setServerAddr(serverLinkAddr)
|
|
||||||
.setMetered(true)
|
|
||||||
.setSingleClientAddr(clientAddr);
|
|
||||||
// TODO: also advertise link MTU
|
|
||||||
|
|
||||||
mDhcpServerStartIndex++;
|
mDhcpServerStartIndex++;
|
||||||
mDeps.makeDhcpServer(
|
mDeps.makeDhcpServer(
|
||||||
mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
|
mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
|
||||||
@@ -570,7 +596,7 @@ public class IpServer extends StateMachine {
|
|||||||
});
|
});
|
||||||
mDhcpServer = null;
|
mDhcpServer = null;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
mLog.e("Error stopping DHCP", e);
|
mLog.e("Error stopping DHCP server", e);
|
||||||
// Not much more we can do here
|
// Not much more we can do here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,31 +678,33 @@ public class IpServer extends StateMachine {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directly-connected route.
|
|
||||||
final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
|
|
||||||
mIpv4Address.getPrefixLength());
|
|
||||||
final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
mLinkProperties.addLinkAddress(mIpv4Address);
|
mLinkProperties.addLinkAddress(mIpv4Address);
|
||||||
mLinkProperties.addRoute(route);
|
mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));
|
||||||
} else {
|
} else {
|
||||||
mLinkProperties.removeLinkAddress(mIpv4Address);
|
mLinkProperties.removeLinkAddress(mIpv4Address);
|
||||||
mLinkProperties.removeRoute(route);
|
mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRandomWifiIPv4Address() {
|
private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) {
|
||||||
|
final byte[] ipv4Addr = rawAddr;
|
||||||
|
ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
|
||||||
try {
|
try {
|
||||||
byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
|
return (Inet4Address) InetAddress.getByAddress(ipv4Addr);
|
||||||
bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
|
} catch (UnknownHostException e) {
|
||||||
return InetAddress.getByAddress(bytes).getHostAddress();
|
mLog.e("Failed to construct Inet4Address from raw IPv4 addr");
|
||||||
} catch (Exception e) {
|
return null;
|
||||||
return WIFI_HOST_IFACE_ADDR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getRandomWifiIPv4Address() {
|
||||||
|
final Inet4Address ipv4Addr =
|
||||||
|
getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress());
|
||||||
|
return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean startIPv6() {
|
private boolean startIPv6() {
|
||||||
mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
|
mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
|
||||||
if (mInterfaceParams == null) {
|
if (mInterfaceParams == null) {
|
||||||
@@ -761,21 +789,43 @@ public class IpServer extends StateMachine {
|
|||||||
mLastIPv6UpstreamIfindex = upstreamIfindex;
|
mLastIPv6UpstreamIfindex = upstreamIfindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
|
||||||
|
final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
|
||||||
|
mNetd, toBeRemoved);
|
||||||
|
if (removalFailures > 0) {
|
||||||
|
mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
|
||||||
|
removalFailures));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
|
||||||
|
try {
|
||||||
|
// It's safe to call networkAddInterface() even if
|
||||||
|
// the interface is already in the local_network.
|
||||||
|
mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
|
||||||
|
try {
|
||||||
|
// Add routes from local network. Note that adding routes that
|
||||||
|
// already exist does not cause an error (EEXIST is silently ignored).
|
||||||
|
RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (ServiceSpecificException | RemoteException e) {
|
||||||
|
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
|
||||||
|
}
|
||||||
|
|
||||||
private void configureLocalIPv6Routes(
|
private void configureLocalIPv6Routes(
|
||||||
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
|
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
|
||||||
// [1] Remove the routes that are deprecated.
|
// [1] Remove the routes that are deprecated.
|
||||||
if (!deprecatedPrefixes.isEmpty()) {
|
if (!deprecatedPrefixes.isEmpty()) {
|
||||||
final ArrayList<RouteInfo> toBeRemoved =
|
removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes));
|
||||||
getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
|
|
||||||
// Remove routes from local network.
|
|
||||||
final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
|
|
||||||
mNetd, toBeRemoved);
|
|
||||||
if (removalFailures > 0) {
|
|
||||||
mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
|
|
||||||
removalFailures));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [2] Add only the routes that have not previously been added.
|
// [2] Add only the routes that have not previously been added.
|
||||||
@@ -786,24 +836,7 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!addedPrefixes.isEmpty()) {
|
if (!addedPrefixes.isEmpty()) {
|
||||||
final ArrayList<RouteInfo> toBeAdded =
|
addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes));
|
||||||
getLocalRoutesFor(mIfaceName, addedPrefixes);
|
|
||||||
try {
|
|
||||||
// It's safe to call networkAddInterface() even if
|
|
||||||
// the interface is already in the local_network.
|
|
||||||
mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
|
|
||||||
try {
|
|
||||||
// Add routes from local network. Note that adding routes that
|
|
||||||
// already exist does not cause an error (EEXIST is silently ignored).
|
|
||||||
RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
mLog.e("Failed to add IPv6 routes to local table: " + e);
|
|
||||||
}
|
|
||||||
} catch (ServiceSpecificException | RemoteException e) {
|
|
||||||
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -945,6 +978,80 @@ 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) {
|
||||||
|
if (!currentPrefix.contains(mIpv4Address.getAddress())
|
||||||
|
|| currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
|
||||||
|
Log.e(TAG, "Invalid prefix: " + currentPrefix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final LinkAddress deprecatedLinkAddress = mIpv4Address;
|
||||||
|
final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix);
|
||||||
|
if (srvAddr == null) {
|
||||||
|
mLog.e("Fail to request a new downstream prefix");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength());
|
||||||
|
|
||||||
|
// Add new IPv4 address on the interface.
|
||||||
|
if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
|
||||||
|
mLog.e("Failed to add new IP " + srvAddr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove deprecated routes from local network.
|
||||||
|
removeRoutesFromLocalNetwork(
|
||||||
|
Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
|
||||||
|
mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
|
||||||
|
|
||||||
|
// Add new routes to local network.
|
||||||
|
addRoutesToLocalNetwork(
|
||||||
|
Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
|
||||||
|
mLinkProperties.addLinkAddress(mIpv4Address);
|
||||||
|
|
||||||
|
// Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
|
||||||
|
// listen on the interface configured with new IPv4 address, that results DNS validation
|
||||||
|
// failure of downstream client even if appropriate routes have been configured.
|
||||||
|
try {
|
||||||
|
mNetd.tetherApplyDnsInterfaces();
|
||||||
|
} catch (ServiceSpecificException | RemoteException e) {
|
||||||
|
mLog.e("Failed to update local DNS caching server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendLinkProperties();
|
||||||
|
|
||||||
|
// Notify DHCP server that new prefix/route has been applied on IpServer.
|
||||||
|
final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
|
||||||
|
(Inet4Address) mStaticIpv4ClientAddr.getAddress();
|
||||||
|
final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
|
||||||
|
srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
|
||||||
|
try {
|
||||||
|
mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
|
||||||
|
@Override
|
||||||
|
public void callback(int statusCode) {
|
||||||
|
if (statusCode != STATUS_SUCCESS) {
|
||||||
|
mLog.e("Error updating DHCP serving params: " + statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
mLog.e("Error updating DHCP serving params", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private byte getHopLimit(String upstreamIface) {
|
private byte getHopLimit(String upstreamIface) {
|
||||||
try {
|
try {
|
||||||
int upstreamHopLimit = Integer.parseUnsignedInt(
|
int upstreamHopLimit = Integer.parseUnsignedInt(
|
||||||
@@ -1056,11 +1163,9 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
|
NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address));
|
||||||
mIpv4Address.getPrefixLength());
|
|
||||||
NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix);
|
|
||||||
} 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;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1115,6 +1220,9 @@ public class IpServer extends StateMachine {
|
|||||||
mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
||||||
transitionTo(mInitialState);
|
transitionTo(mInitialState);
|
||||||
break;
|
break;
|
||||||
|
case CMD_NEW_PREFIX_REQUEST:
|
||||||
|
handleNewPrefixRequest((IpPrefix) message.obj);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.net.ip;
|
|||||||
|
|
||||||
import static android.net.INetd.IF_STATE_UP;
|
import static android.net.INetd.IF_STATE_UP;
|
||||||
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||||
|
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.TETHERING_WIFI_P2P;
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
@@ -38,6 +39,7 @@ import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
@@ -67,6 +69,7 @@ import android.net.MacAddress;
|
|||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.TetherOffloadRuleParcel;
|
import android.net.TetherOffloadRuleParcel;
|
||||||
import android.net.dhcp.DhcpServingParamsParcel;
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.IDhcpEventCallbacks;
|
||||||
import android.net.dhcp.IDhcpServer;
|
import android.net.dhcp.IDhcpServer;
|
||||||
import android.net.dhcp.IDhcpServerCallbacks;
|
import android.net.dhcp.IDhcpServerCallbacks;
|
||||||
import android.net.ip.IpNeighborMonitor.NeighborEvent;
|
import android.net.ip.IpNeighborMonitor.NeighborEvent;
|
||||||
@@ -94,6 +97,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -496,6 +500,59 @@ public class IpServerTest {
|
|||||||
assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
|
assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startsDhcpServerOnNcm() throws Exception {
|
||||||
|
initStateMachine(TETHERING_NCM);
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
|
||||||
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
|
assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnNewPrefixRequest() throws Exception {
|
||||||
|
initStateMachine(TETHERING_NCM);
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
|
||||||
|
|
||||||
|
final IDhcpEventCallbacks eventCallbacks;
|
||||||
|
final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor =
|
||||||
|
ArgumentCaptor.forClass(IDhcpEventCallbacks.class);
|
||||||
|
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||||
|
any(), dhcpEventCbsCaptor.capture());
|
||||||
|
eventCallbacks = dhcpEventCbsCaptor.getValue();
|
||||||
|
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 =
|
||||||
|
ArgumentCaptor.forClass(LinkProperties.class);
|
||||||
|
InOrder inOrder = inOrder(mNetd, mCallback);
|
||||||
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
|
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);
|
||||||
|
// One for ipv4 route, one for ipv6 link local route.
|
||||||
|
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
||||||
|
any(), any());
|
||||||
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
|
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
|
||||||
|
verifyNoMoreInteractions(mCallback);
|
||||||
|
|
||||||
|
final LinkProperties linkProperties = lpCaptor.getValue();
|
||||||
|
final List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
|
||||||
|
assertEquals(1, linkProperties.getLinkAddresses().size());
|
||||||
|
assertEquals(1, linkProperties.getRoutes().size());
|
||||||
|
final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(),
|
||||||
|
linkAddresses.get(0).getPrefixLength());
|
||||||
|
assertNotEquals(prefix, new IpPrefix("192.168.42.0/24"));
|
||||||
|
|
||||||
|
verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any());
|
||||||
|
assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doesNotStartDhcpServerIfDisabled() throws Exception {
|
public void doesNotStartDhcpServerIfDisabled() throws Exception {
|
||||||
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
|
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
|
||||||
@@ -731,19 +788,26 @@ public class IpServerTest {
|
|||||||
verify(mIpNeighborMonitor, never()).start();
|
verify(mIpNeighborMonitor, never()).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
|
private void assertDhcpServingParams(final DhcpServingParamsParcel params,
|
||||||
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
|
final IpPrefix prefix) {
|
||||||
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
|
|
||||||
any(), any());
|
|
||||||
final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
|
|
||||||
// Last address byte is random
|
// Last address byte is random
|
||||||
assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
|
assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr)));
|
||||||
assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
|
assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength);
|
||||||
assertEquals(1, params.defaultRouters.length);
|
assertEquals(1, params.defaultRouters.length);
|
||||||
assertEquals(params.serverAddr, params.defaultRouters[0]);
|
assertEquals(params.serverAddr, params.defaultRouters[0]);
|
||||||
assertEquals(1, params.dnsServers.length);
|
assertEquals(1, params.dnsServers.length);
|
||||||
assertEquals(params.serverAddr, params.dnsServers[0]);
|
assertEquals(params.serverAddr, params.dnsServers[0]);
|
||||||
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
|
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
|
||||||
|
if (mIpServer.interfaceType() == TETHERING_NCM) {
|
||||||
|
assertTrue(params.changePrefixOnDecline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
|
||||||
|
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
|
||||||
|
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||||
|
any(), any());
|
||||||
|
assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user