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