Merge "Use InterfaceSet for upstream interfaces."
This commit is contained in:
@@ -56,6 +56,7 @@ import android.net.NetworkInfo;
|
||||
import android.net.NetworkState;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.InterfaceSet;
|
||||
import android.net.util.PrefixUtils;
|
||||
import android.net.util.SharedLog;
|
||||
import android.net.util.VersionedBroadcastListener;
|
||||
@@ -98,6 +99,7 @@ import com.android.server.connectivity.tethering.SimChangeListener;
|
||||
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
|
||||
import com.android.server.connectivity.tethering.TetheringConfiguration;
|
||||
import com.android.server.connectivity.tethering.TetheringDependencies;
|
||||
import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
|
||||
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
|
||||
import com.android.server.net.BaseNetworkObserver;
|
||||
|
||||
@@ -184,7 +186,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
private final TetheringDependencies mDeps;
|
||||
|
||||
private volatile TetheringConfiguration mConfig;
|
||||
private String mCurrentUpstreamIface;
|
||||
private InterfaceSet mCurrentUpstreamIfaceSet;
|
||||
private Notification.Builder mTetheredNotificationBuilder;
|
||||
private int mLastNotificationId;
|
||||
|
||||
@@ -1161,12 +1163,11 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
|
||||
// Needed because the canonical source of upstream truth is just the
|
||||
// upstream interface name, |mCurrentUpstreamIface|. This is ripe for
|
||||
// future simplification, once the upstream Network is canonical.
|
||||
// upstream interface set, |mCurrentUpstreamIfaceSet|.
|
||||
private boolean pertainsToCurrentUpstream(NetworkState ns) {
|
||||
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
|
||||
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
|
||||
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
|
||||
if (mCurrentUpstreamIface.equals(ifname)) {
|
||||
if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1361,31 +1362,27 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
|
||||
protected void setUpstreamNetwork(NetworkState ns) {
|
||||
String iface = null;
|
||||
InterfaceSet ifaces = null;
|
||||
if (ns != null) {
|
||||
// Find the interface with the default IPv4 route. It may be the
|
||||
// interface described by linkProperties, or one of the interfaces
|
||||
// stacked on top of it.
|
||||
mLog.i("Looking for default routes on: " + ns.linkProperties);
|
||||
final String iface4 = getIPv4DefaultRouteInterface(ns);
|
||||
final String iface6 = getIPv6DefaultRouteInterface(ns);
|
||||
mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
|
||||
|
||||
iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
|
||||
ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
|
||||
mLog.i("Found upstream interface(s): " + ifaces);
|
||||
}
|
||||
|
||||
if (iface != null) {
|
||||
if (ifaces != null) {
|
||||
setDnsForwarders(ns.network, ns.linkProperties);
|
||||
}
|
||||
notifyDownstreamsOfNewUpstreamIface(iface);
|
||||
notifyDownstreamsOfNewUpstreamIface(ifaces);
|
||||
if (ns != null && pertainsToCurrentUpstream(ns)) {
|
||||
// If we already have NetworkState for this network examine
|
||||
// it immediately, because there likely will be no second
|
||||
// EVENT_ON_AVAILABLE (it was already received).
|
||||
handleNewUpstreamNetworkState(ns);
|
||||
} else if (mCurrentUpstreamIface == null) {
|
||||
// There are no available upstream networks, or none that
|
||||
// have an IPv4 default route (current metric for success).
|
||||
} else if (mCurrentUpstreamIfaceSet == null) {
|
||||
// There are no available upstream networks.
|
||||
handleNewUpstreamNetworkState(null);
|
||||
}
|
||||
}
|
||||
@@ -1412,12 +1409,10 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
|
||||
mLog.log("Notifying downstreams of upstream=" + ifaceName);
|
||||
mCurrentUpstreamIface = ifaceName;
|
||||
protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
|
||||
mCurrentUpstreamIfaceSet = ifaces;
|
||||
for (TetherInterfaceStateMachine sm : mNotifyList) {
|
||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
||||
ifaceName);
|
||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1489,7 +1484,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
// For example, after CONNECTIVITY_ACTION listening is removed, here
|
||||
// is where we could observe a Wi-Fi network becoming available and
|
||||
// passing validation.
|
||||
if (mCurrentUpstreamIface == null) {
|
||||
if (mCurrentUpstreamIfaceSet == null) {
|
||||
// If we have no upstream interface, try to run through upstream
|
||||
// selection again. If, for example, IPv4 connectivity has shown up
|
||||
// after IPv6 (e.g., 464xlat became available) we want the chance to
|
||||
@@ -1513,8 +1508,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
handleNewUpstreamNetworkState(ns);
|
||||
break;
|
||||
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
|
||||
setDnsForwarders(ns.network, ns.linkProperties);
|
||||
handleNewUpstreamNetworkState(ns);
|
||||
chooseUpstreamType(false);
|
||||
break;
|
||||
case UpstreamNetworkMonitor.EVENT_ON_LOST:
|
||||
// TODO: Re-evaluate possible upstreams. Currently upstream
|
||||
@@ -1587,7 +1581,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
|
||||
handleInterfaceServingStateActive(message.arg1, who);
|
||||
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
||||
mCurrentUpstreamIface);
|
||||
mCurrentUpstreamIfaceSet);
|
||||
// If there has been a change and an upstream is now
|
||||
// desired, kick off the selection process.
|
||||
final boolean previousUpstreamWanted = updateUpstreamWanted();
|
||||
@@ -1865,7 +1859,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
pw.println(" - lastError = " + tetherState.lastError);
|
||||
}
|
||||
pw.println("Upstream wanted: " + upstreamWanted());
|
||||
pw.println("Current upstream interface: " + mCurrentUpstreamIface);
|
||||
pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,8 @@ import android.util.Log;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Random;
|
||||
|
||||
@@ -119,7 +117,7 @@ public class IPv6TetheringCoordinator {
|
||||
if (VDBG) {
|
||||
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
|
||||
}
|
||||
if (!canTetherIPv6(ns, mLog)) {
|
||||
if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
|
||||
stopIPv6TetheringOnAllInterfaces();
|
||||
setUpstreamNetworkState(null);
|
||||
return;
|
||||
@@ -208,70 +206,6 @@ public class IPv6TetheringCoordinator {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
|
||||
// Broadly speaking:
|
||||
//
|
||||
// [1] does the upstream have an IPv6 default route?
|
||||
//
|
||||
// and
|
||||
//
|
||||
// [2] does the upstream have one or more global IPv6 /64s
|
||||
// dedicated to this device?
|
||||
//
|
||||
// In lieu of Prefix Delegation and other evaluation of whether a
|
||||
// prefix may or may not be dedicated to this device, for now just
|
||||
// check whether the upstream is TRANSPORT_CELLULAR. This works
|
||||
// because "[t]he 3GPP network allocates each default bearer a unique
|
||||
// /64 prefix", per RFC 6459, Section 5.2.
|
||||
|
||||
final boolean canTether =
|
||||
(ns != null) && (ns.network != null) &&
|
||||
(ns.linkProperties != null) && (ns.networkCapabilities != null) &&
|
||||
// At least one upstream DNS server:
|
||||
ns.linkProperties.isProvisioned() &&
|
||||
// Minimal amount of IPv6 provisioning:
|
||||
ns.linkProperties.hasIPv6DefaultRoute() &&
|
||||
ns.linkProperties.hasGlobalIPv6Address() &&
|
||||
// Temporary approximation of "dedicated prefix":
|
||||
ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
|
||||
|
||||
// For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
|
||||
// tethering with 464xlat involved). TODO: Rectify this shortcoming,
|
||||
// likely by calling NetworkManagementService#startInterfaceForwarding()
|
||||
// for all upstream interfaces.
|
||||
RouteInfo v4default = null;
|
||||
RouteInfo v6default = null;
|
||||
if (canTether) {
|
||||
for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
|
||||
if (r.isIPv4Default()) {
|
||||
v4default = r;
|
||||
} else if (r.isIPv6Default()) {
|
||||
v6default = r;
|
||||
}
|
||||
|
||||
if (v4default != null && v6default != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean supportedConfiguration =
|
||||
(v4default != null) && (v6default != null) &&
|
||||
(v4default.getInterface() != null) &&
|
||||
v4default.getInterface().equals(v6default.getInterface());
|
||||
|
||||
final boolean outcome = canTether && supportedConfiguration;
|
||||
|
||||
if (ns == null) {
|
||||
sharedLog.log("No available upstream.");
|
||||
} else {
|
||||
sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
|
||||
(outcome ? "available" : "not available"), toDebugString(ns)));
|
||||
}
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
|
||||
final LinkProperties v6only = new LinkProperties();
|
||||
if (lp == null) {
|
||||
|
||||
@@ -31,7 +31,7 @@ import android.net.ip.InterfaceController;
|
||||
import android.net.ip.RouterAdvertisementDaemon;
|
||||
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
||||
import android.net.util.InterfaceParams;
|
||||
import android.net.util.NetdService;
|
||||
import android.net.util.InterfaceSet;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.Looper;
|
||||
@@ -49,12 +49,12 @@ import com.android.internal.util.StateMachine;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides the interface to IP-layer serving functionality for a given network
|
||||
@@ -121,7 +121,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
||||
|
||||
private int mLastError;
|
||||
private int mServingMode;
|
||||
private String mMyUpstreamIfaceName; // may change over time
|
||||
private InterfaceSet mUpstreamIfaceSet; // may change over time
|
||||
private InterfaceParams mInterfaceParams;
|
||||
// TODO: De-duplicate this with mLinkProperties above. Currently, these link
|
||||
// properties are those selected by the IPv6TetheringCoordinator and relayed
|
||||
@@ -622,10 +622,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
||||
}
|
||||
|
||||
private void cleanupUpstream() {
|
||||
if (mMyUpstreamIfaceName == null) return;
|
||||
if (mUpstreamIfaceSet == null) return;
|
||||
|
||||
cleanupUpstreamInterface(mMyUpstreamIfaceName);
|
||||
mMyUpstreamIfaceName = null;
|
||||
for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
|
||||
mUpstreamIfaceSet = null;
|
||||
}
|
||||
|
||||
private void cleanupUpstreamInterface(String upstreamIface) {
|
||||
@@ -661,34 +661,66 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
||||
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
|
||||
break;
|
||||
case CMD_TETHER_CONNECTION_CHANGED:
|
||||
String newUpstreamIfaceName = (String)(message.obj);
|
||||
if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
|
||||
(mMyUpstreamIfaceName != null &&
|
||||
mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
|
||||
final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
|
||||
if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
|
||||
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
|
||||
break;
|
||||
}
|
||||
cleanupUpstream();
|
||||
if (newUpstreamIfaceName != null) {
|
||||
|
||||
if (newUpstreamIfaceSet == null) {
|
||||
cleanupUpstream();
|
||||
break;
|
||||
}
|
||||
|
||||
for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
|
||||
cleanupUpstreamInterface(removed);
|
||||
}
|
||||
|
||||
final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
|
||||
// This makes the call to cleanupUpstream() in the error
|
||||
// path for any interface neatly cleanup all the interfaces.
|
||||
mUpstreamIfaceSet = newUpstreamIfaceSet;
|
||||
|
||||
for (String ifname : added) {
|
||||
try {
|
||||
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
|
||||
mNMService.startInterfaceForwarding(mIfaceName,
|
||||
newUpstreamIfaceName);
|
||||
mNMService.enableNat(mIfaceName, ifname);
|
||||
mNMService.startInterfaceForwarding(mIfaceName, ifname);
|
||||
} catch (Exception e) {
|
||||
mLog.e("Exception enabling NAT: " + e);
|
||||
cleanupUpstreamInterface(newUpstreamIfaceName);
|
||||
cleanupUpstream();
|
||||
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
|
||||
transitionTo(mInitialState);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mMyUpstreamIfaceName = newUpstreamIfaceName;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
|
||||
if (mUpstreamIfaceSet == null && newIfaces == null) return true;
|
||||
if (mUpstreamIfaceSet != null && newIfaces != null) {
|
||||
return mUpstreamIfaceSet.equals(newIfaces);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
|
||||
if (mUpstreamIfaceSet == null) return new HashSet<>();
|
||||
|
||||
final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
|
||||
removed.removeAll(newIfaces.ifnames);
|
||||
return removed;
|
||||
}
|
||||
|
||||
private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
|
||||
final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
|
||||
if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.server.connectivity.tethering;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkState;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.InterfaceSet;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class TetheringInterfaceUtils {
|
||||
/**
|
||||
* Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
|
||||
* @return null if there is no usable interface, or a set of at least one interface otherwise.
|
||||
*/
|
||||
public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
|
||||
if (ns == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final LinkProperties lp = ns.linkProperties;
|
||||
final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
|
||||
final String if6 = getIPv6Interface(ns);
|
||||
|
||||
return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upstream interface for IPv6 tethering.
|
||||
* @return null if there is no usable interface, or the interface name otherwise.
|
||||
*/
|
||||
public static @Nullable String getIPv6Interface(NetworkState ns) {
|
||||
// Broadly speaking:
|
||||
//
|
||||
// [1] does the upstream have an IPv6 default route?
|
||||
//
|
||||
// and
|
||||
//
|
||||
// [2] does the upstream have one or more global IPv6 /64s
|
||||
// dedicated to this device?
|
||||
//
|
||||
// In lieu of Prefix Delegation and other evaluation of whether a
|
||||
// prefix may or may not be dedicated to this device, for now just
|
||||
// check whether the upstream is TRANSPORT_CELLULAR. This works
|
||||
// because "[t]he 3GPP network allocates each default bearer a unique
|
||||
// /64 prefix", per RFC 6459, Section 5.2.
|
||||
final boolean canTether =
|
||||
(ns != null) && (ns.network != null) &&
|
||||
(ns.linkProperties != null) && (ns.networkCapabilities != null) &&
|
||||
// At least one upstream DNS server:
|
||||
ns.linkProperties.hasIPv6DnsServer() &&
|
||||
// Minimal amount of IPv6 provisioning:
|
||||
ns.linkProperties.hasGlobalIPv6Address() &&
|
||||
// Temporary approximation of "dedicated prefix":
|
||||
ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
|
||||
|
||||
return canTether
|
||||
? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
|
||||
: null;
|
||||
}
|
||||
|
||||
private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
|
||||
final RouteInfo ri = (lp != null)
|
||||
? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
|
||||
: null;
|
||||
return (ri != null) ? ri.getInterface() : null;
|
||||
}
|
||||
}
|
||||
52
services/net/java/android/net/util/InterfaceSet.java
Normal file
52
services/net/java/android/net/util/InterfaceSet.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.net.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class InterfaceSet {
|
||||
public final Set<String> ifnames;
|
||||
|
||||
public InterfaceSet(String... names) {
|
||||
final Set<String> nameSet = new HashSet<>();
|
||||
for (String name : names) {
|
||||
if (name != null) nameSet.add(name);
|
||||
}
|
||||
ifnames = Collections.unmodifiableSet(nameSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringJoiner sj = new StringJoiner(",", "[", "]");
|
||||
for (String ifname : ifnames) sj.add(ifname);
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj != null
|
||||
&& obj instanceof InterfaceSet
|
||||
&& ifnames.equals(((InterfaceSet)obj).ifnames);
|
||||
}
|
||||
}
|
||||
61
tests/net/java/android/net/util/InterfaceSetTest.java
Normal file
61
tests/net/java/android/net/util/InterfaceSetTest.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.net.util;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class InterfaceSetTest {
|
||||
@Test
|
||||
public void testNullNamesIgnored() {
|
||||
final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
|
||||
assertEquals(2, set.ifnames.size());
|
||||
assertTrue(set.ifnames.contains("if1"));
|
||||
assertTrue(set.ifnames.contains("if2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
final InterfaceSet set = new InterfaceSet("if1", "if2");
|
||||
final String setString = set.toString();
|
||||
assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString_Empty() {
|
||||
final InterfaceSet set = new InterfaceSet(null, null);
|
||||
assertEquals("[]", set.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
|
||||
assertEquals(new InterfaceSet(null, null), new InterfaceSet());
|
||||
assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
|
||||
assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
|
||||
assertFalse(new InterfaceSet().equals(null));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
|
||||
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
@@ -225,7 +224,8 @@ public class TetheringTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6) {
|
||||
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
|
||||
boolean with464xlat) {
|
||||
final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null);
|
||||
info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
|
||||
final LinkProperties prop = new LinkProperties();
|
||||
@@ -245,6 +245,15 @@ public class TetheringTest {
|
||||
NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
|
||||
}
|
||||
|
||||
if (with464xlat) {
|
||||
final LinkProperties stackedLink = new LinkProperties();
|
||||
stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
|
||||
stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
|
||||
NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
|
||||
|
||||
prop.addStackedLink(stackedLink);
|
||||
}
|
||||
|
||||
|
||||
final NetworkCapabilities capabilities = new NetworkCapabilities()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
|
||||
@@ -252,11 +261,19 @@ public class TetheringTest {
|
||||
}
|
||||
|
||||
private static NetworkState buildMobileIPv4UpstreamState() {
|
||||
return buildMobileUpstreamState(true, false);
|
||||
return buildMobileUpstreamState(true, false, false);
|
||||
}
|
||||
|
||||
private static NetworkState buildMobileIPv6UpstreamState() {
|
||||
return buildMobileUpstreamState(false, true, false);
|
||||
}
|
||||
|
||||
private static NetworkState buildMobileDualStackUpstreamState() {
|
||||
return buildMobileUpstreamState(true, true);
|
||||
return buildMobileUpstreamState(true, true, false);
|
||||
}
|
||||
|
||||
private static NetworkState buildMobile464xlatUpstreamState() {
|
||||
return buildMobileUpstreamState(false, true, true);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -521,7 +538,7 @@ public class TetheringTest {
|
||||
|
||||
for (TetherInterfaceStateMachine tism :
|
||||
mTetheringDependencies.ipv6CoordinatorNotifyList) {
|
||||
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true);
|
||||
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
|
||||
tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
|
||||
upstreamState.linkProperties.isIPv6Provisioned()
|
||||
? ipv6OnlyState.linkProperties
|
||||
@@ -548,6 +565,19 @@ public class TetheringTest {
|
||||
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingMobileUsbTethering_IPv6() throws Exception {
|
||||
NetworkState upstreamState = buildMobileIPv6UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingMobileUsbTethering_DualStack() throws Exception {
|
||||
NetworkState upstreamState = buildMobileDualStackUpstreamState();
|
||||
@@ -562,6 +592,52 @@ public class TetheringTest {
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
|
||||
NetworkState upstreamState = buildMobile464xlatUpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
|
||||
TEST_XLAT_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
|
||||
// Setup IPv6
|
||||
NetworkState upstreamState = buildMobileIPv6UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
// Then 464xlat comes up
|
||||
upstreamState = buildMobile464xlatUpstreamState();
|
||||
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
|
||||
.thenReturn(upstreamState);
|
||||
|
||||
// Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
|
||||
mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
|
||||
Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
|
||||
UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
|
||||
0,
|
||||
upstreamState);
|
||||
mLooper.dispatchAll();
|
||||
|
||||
// Forwarding is added for 464xlat
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
|
||||
TEST_XLAT_MOBILE_IFNAME);
|
||||
// Forwarding was not re-added for v6 (still times(1))
|
||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.net.InterfaceConfiguration;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.InterfaceSet;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.RemoteException;
|
||||
@@ -371,7 +372,7 @@ public class TetherInterfaceStateMachineTest {
|
||||
*/
|
||||
private void dispatchTetherConnectionChanged(String upstreamIface) {
|
||||
mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
||||
upstreamIface);
|
||||
new InterfaceSet(upstreamIface));
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user