Merge "Add tests for IPv4 of dual stack tethering." into pi-dev

This commit is contained in:
Remi NGUYEN VAN
2018-03-30 09:13:50 +00:00
committed by Android (Google) Code Review
6 changed files with 296 additions and 89 deletions

View File

@@ -181,6 +181,7 @@ public class Tethering extends BaseNetworkObserver {
private final VersionedBroadcastListener mCarrierConfigChange;
// TODO: Delete SimChangeListener; it's obsolete.
private final SimChangeListener mSimChange;
private final TetheringDependencies mDeps;
private volatile TetheringConfiguration mConfig;
private String mCurrentUpstreamIface;
@@ -202,12 +203,13 @@ public class Tethering extends BaseNetworkObserver {
mPolicyManager = policyManager;
mLooper = looper;
mSystemProperties = systemProperties;
mDeps = deps;
mPublicSync = new Object();
mTetherStates = new ArrayMap<>();
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
final Handler smHandler = mTetherMasterSM.getHandler();
@@ -215,8 +217,8 @@ public class Tethering extends BaseNetworkObserver {
deps.getOffloadHardwareInterface(smHandler, mLog),
mContext.getContentResolver(), mNMService,
mLog);
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
IntentFilter filter = new IntentFilter();
@@ -1241,7 +1243,7 @@ public class Tethering extends BaseNetworkObserver {
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
TetherMasterSM(String name, Looper looper) {
TetherMasterSM(String name, Looper looper, TetheringDependencies deps) {
super(name, looper);
mInitialState = new InitialState();
@@ -1261,7 +1263,7 @@ public class Tethering extends BaseNetworkObserver {
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
setInitialState(mInitialState);
@@ -1997,7 +1999,7 @@ public class Tethering extends BaseNetworkObserver {
final TetherState tetherState = new TetherState(
new TetherInterfaceStateMachine(
iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(iface)));
makeControlCallback(iface), mDeps));
mTetherStates.put(iface, tetherState);
tetherState.stateMachine.start();
}

View File

@@ -117,6 +117,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
private final int mInterfaceType;
private final LinkProperties mLinkProperties;
private final TetheringDependencies mDeps;
private int mLastError;
private int mServingMode;
private String mMyUpstreamIfaceName; // may change over time
@@ -134,18 +136,19 @@ public class TetherInterfaceStateMachine extends StateMachine {
public TetherInterfaceStateMachine(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
IControlsTethering tetherController) {
IControlsTethering tetherController,
TetheringDependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
// TODO: This should be passed in for testability.
mNetd = NetdService.getInstance();
mNetd = deps.getNetdService();
mStatsService = statsService;
mTetherController = tetherController;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mServingMode = IControlsTethering.STATE_AVAILABLE;
@@ -246,16 +249,14 @@ public class TetherInterfaceStateMachine extends StateMachine {
}
private boolean startIPv6() {
// TODO: Refactor for better testability. This is one of the things
// that prohibits unittesting IPv6 tethering setup.
mInterfaceParams = InterfaceParams.getByName(mIfaceName);
mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
if (mInterfaceParams == null) {
mLog.e("Failed to find InterfaceParams");
stopIPv6();
return false;
}
mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
if (!mRaDaemon.start()) {
stopIPv6();
return false;

View File

@@ -16,9 +16,18 @@
package com.android.server.connectivity.tethering;
import android.content.Context;
import android.net.INetd;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
import android.os.Handler;
import android.net.util.SharedLog;
import com.android.internal.util.StateMachine;
import java.util.ArrayList;
/**
* Capture tethering dependencies, for injection.
@@ -29,4 +38,26 @@ public class TetheringDependencies {
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
SharedLog log, int what) {
return new UpstreamNetworkMonitor(ctx, target, log, what);
}
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
public InterfaceParams getInterfaceParams(String ifName) {
return InterfaceParams.getByName(ifName);
}
public INetd getNetdService() {
return NetdService.getInstance();
}
}

View File

@@ -27,8 +27,12 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
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;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -36,6 +40,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -45,18 +50,29 @@ import static org.mockito.Mockito.mock;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.NetworkRequest;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -74,10 +90,16 @@ import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
import android.test.mock.MockContentResolver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import org.junit.After;
import org.junit.Before;
@@ -86,13 +108,21 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetheringTest {
private static final int IFINDEX_OFFSET = 100;
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
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_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@@ -103,16 +133,21 @@ public class TetheringTest {
@Mock private MockableSystemProperties mSystemProperties;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
@Mock private TetheringDependencies mTetheringDependencies;
@Mock private UsbManager mUsbManager;
@Mock private WifiManager mWifiManager;
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
@Mock private INetd mNetd;
private final MockTetheringDependencies mTetheringDependencies =
new MockTetheringDependencies();
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
private final String mTestIfname = "test_wlan0";
private Vector<Intent> mIntents;
private BroadcastInterceptingContext mServiceContext;
@@ -146,23 +181,104 @@ public class TetheringTest {
}
}
public class MockTetheringDependencies extends TetheringDependencies {
private StateMachine upstreamNetworkMonitorMasterSM;
private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
@Override
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return mOffloadHardwareInterface;
}
@Override
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
StateMachine target, SharedLog log, int what) {
upstreamNetworkMonitorMasterSM = target;
return mUpstreamNetworkMonitor;
}
@Override
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
ipv6CoordinatorNotifyList = notifyList;
return mIPv6TetheringCoordinator;
}
@Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return mRouterAdvertisementDaemon;
}
@Override
public INetd getNetdService() {
return mNetd;
}
@Override
public InterfaceParams getInterfaceParams(String ifName) {
final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
TEST_MOBILE_IFNAME };
final int index = ArrayUtils.indexOf(ifaces, ifName);
assertTrue("Non-mocked interface: " + ifName, index >= 0);
return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6) {
final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null);
info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_MOBILE_IFNAME);
if (withIPv4) {
prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
}
if (withIPv6) {
prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
prop.addLinkAddress(
new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
NetworkConstants.RFC7421_PREFIX_LENGTH));
prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
}
final NetworkCapabilities capabilities = new NetworkCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
}
private static NetworkState buildMobileIPv4UpstreamState() {
return buildMobileUpstreamState(true, false);
}
private static NetworkState buildMobileDualStackUpstreamState() {
return buildMobileUpstreamState(true, true);
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
.thenReturn(new String[0]);
.thenReturn(new String[] { "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
.thenReturn(new String[]{ "test_wlan\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
when(mNMService.listInterfaces())
.thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
when(mNMService.getInterfaceConfig(anyString()))
.thenReturn(new InterfaceConfiguration());
when(mRouterAdvertisementDaemon.start())
.thenReturn(true);
mServiceContext = new MockContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
@@ -176,8 +292,6 @@ public class TetheringTest {
};
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
when(mTetheringDependencies.getOffloadHardwareInterface(
any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
mLooper.getLooper(), mSystemProperties,
mTetheringDependencies);
@@ -264,10 +378,10 @@ public class TetheringTest {
}
private void verifyInterfaceServingModeStarted() throws Exception {
verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(1))
.setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).tetherInterface(mTestIfname);
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
}
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -287,7 +401,7 @@ public class TetheringTest {
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
mTethering.interfaceStatusChanged(mTestIfname, true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
@@ -297,27 +411,31 @@ public class TetheringTest {
// broadcast indicating that the interface is "available".
if (emulateInterfaceStatusChanged) {
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
}
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
@Test
public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
private void prepareUsbTethering(NetworkState upstreamState) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(TETHERING_USB, null, false);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
// Pretend we receive a USB connected broadcast. Here we also pretend
// that the RNDIS function is somehow enabled, so that we see if we
// might trip ourselves up.
sendUsbBroadcast(true, false, true);
mLooper.dispatchAll();
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
}
@Test
public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
NetworkState upstreamState = buildMobileIPv4UpstreamState();
prepareUsbTethering(upstreamState);
// This should produce no activity of any kind.
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
@@ -328,6 +446,10 @@ public class TetheringTest {
// Now we should see the start of tethering mechanics (in this case:
// tetherMatchingInterfaces() which starts by fetching all interfaces).
verify(mNMService, times(1)).listInterfaces();
// UpstreamNetworkMonitor should receive selected upstream
verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
@Test
@@ -348,26 +470,21 @@ public class TetheringTest {
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
mTethering.interfaceStatusChanged(mTestIfname, true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
// UpstreamNetworkMonitor will be started, and will register two callbacks:
// a "listen all" and a "track default".
verify(mConnectivityManager, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
any(NetworkCallback.class), any(Handler.class));
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).start();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyNoMoreInteractions(mConnectivityManager);
@@ -375,14 +492,14 @@ public class TetheringTest {
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
mTethering.interfaceRemoved(mTestIfname);
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(mTestIfname);
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
.setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
@@ -390,9 +507,62 @@ public class TetheringTest {
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
mTethering.getLastTetherError(mTestIfname));
mTethering.getLastTetherError(TEST_WLAN_IFNAME));
}
/**
* Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
*/
private void sendIPv6TetherUpdates(NetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IControlsTethering.STATE_TETHERED));
for (TetherInterfaceStateMachine tism :
mTetheringDependencies.ipv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true);
tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIPv6Provisioned()
? ipv6OnlyState.linkProperties
: null);
}
mLooper.dispatchAll();
}
private void runUsbTethering(NetworkState upstreamState) {
prepareUsbTethering(upstreamState);
sendUsbBroadcast(true, true, true);
mLooper.dispatchAll();
}
@Test
public void workingMobileUsbTethering_IPv4() throws Exception {
NetworkState upstreamState = buildMobileIPv4UpstreamState();
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, never()).buildNewRa(any(), notNull());
}
@Test
public void workingMobileUsbTethering_DualStack() throws Exception {
NetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
@Test
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
workingLocalOnlyHotspotEnrichedApBroadcast(true);
@@ -420,12 +590,12 @@ public class TetheringTest {
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
mTethering.interfaceStatusChanged(mTestIfname, true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
@@ -448,30 +618,23 @@ public class TetheringTest {
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
mTethering.interfaceStatusChanged(mTestIfname, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
// UpstreamNetworkMonitor will be started, and will register two callbacks:
// a "listen all" and a "track default".
verify(mConnectivityManager, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
any(NetworkCallback.class), any(Handler.class));
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_TETHER);
verify(mUpstreamNetworkMonitor, times(1)).start();
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mConnectivityManager, times(1)).requestNetwork(
any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
any(Handler.class));
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyNoMoreInteractions(mConnectivityManager);
@@ -494,14 +657,14 @@ public class TetheringTest {
// Emulate externally-visible WifiManager effects, when tethering mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
mTethering.interfaceRemoved(mTestIfname);
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(mTestIfname);
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
.setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
@@ -509,7 +672,7 @@ public class TetheringTest {
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
mTethering.getLastTetherError(mTestIfname));
mTethering.getLastTetherError(TEST_WLAN_IFNAME));
}
// TODO: Test with and without interfaceStatusChanged().
@@ -530,21 +693,21 @@ public class TetheringTest {
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
mTethering.interfaceStatusChanged(mTestIfname, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
// We verify get/set called twice here: once for setup and once during
// teardown because all events happen over the course of the single
// dispatchAll() above.
verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(2))
.setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).tetherInterface(mTestIfname);
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
// This never gets called because of the exception thrown above.
@@ -552,9 +715,9 @@ public class TetheringTest {
// When the master state machine transitions to an error state it tells
// downstream interfaces, which causes us to tell Wi-Fi about the error
// so it can take down AP mode.
verify(mNMService, times(1)).untetherInterface(mTestIfname);
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mConnectivityManager);
@@ -596,7 +759,7 @@ public class TetheringTest {
@Test
public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = false;
final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 1;
@@ -618,7 +781,7 @@ public class TetheringTest {
@Test
public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = true;
final boolean nextDisallow = false;
final int expectedInteractionsWithShowNotification = 0;
@@ -629,7 +792,7 @@ public class TetheringTest {
@Test
public void testDisallowTetheringUnchanged() throws Exception {
final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final int expectedInteractionsWithShowNotification = 0;
boolean currDisallow = true;
boolean nextDisallow = true;

View File

@@ -31,7 +31,6 @@ import static org.mockito.Mockito.when;
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
@@ -39,7 +38,6 @@ import static com.android.server.connectivity.tethering.IControlsTethering.STATE
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -75,6 +73,7 @@ public class TetherInterfaceStateMachineTest {
@Mock private IControlsTethering mTetherHelper;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
@Mock private TetheringDependencies mTetheringDependencies;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -84,7 +83,7 @@ public class TetherInterfaceStateMachineTest {
private void initStateMachine(int interfaceType) throws Exception {
mTestedSm = new TetherInterfaceStateMachine(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
mNMService, mStatsService, mTetherHelper);
mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
@@ -111,7 +110,8 @@ public class TetherInterfaceStateMachineTest {
@Test
public void startsOutAvailable() {
mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper);
TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
mTetheringDependencies);
mTestedSm.start();
mLooper.dispatchAll();
verify(mTetherHelper).updateInterfaceState(
@@ -346,7 +346,7 @@ public class TetherInterfaceStateMachineTest {
* Send a command to the state machine under test, and run the event loop to idle.
*
* @param command One of the TetherInterfaceStateMachine.CMD_* constants.
* @param obj An additional argument to pass.
* @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
mTestedSm.sendMessage(command, arg1);

View File

@@ -146,6 +146,16 @@ public class UpstreamNetworkMonitorTest {
assertTrue(mCM.hasNoCallbacks());
}
@Test
public void testCallbacksRegistered() {
mUNM.start();
verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
mUNM.stop();
verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
}
@Test
public void testRequestsMobileNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());