Merge "Use InterfaceSet for upstream interfaces."

This commit is contained in:
Remi NGUYEN VAN
2018-03-30 01:05:38 +00:00
committed by Gerrit Code Review
8 changed files with 356 additions and 116 deletions

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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;
}
}
/**

View File

@@ -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;
}
}

View 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);
}
}

View 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));
}
}

View File

@@ -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 {

View File

@@ -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();
}