1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 import static android.hardware.usb.UsbManager.USB_CONFIGURED; 20 import static android.hardware.usb.UsbManager.USB_CONNECTED; 21 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; 22 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; 23 import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; 24 import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; 25 import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; 26 import static android.net.ConnectivityManager.TETHERING_USB; 27 import static android.net.ConnectivityManager.TETHERING_WIFI; 28 import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; 29 import static android.net.ConnectivityManager.TYPE_MOBILE; 30 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; 31 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; 32 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; 33 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; 34 import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; 35 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; 36 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; 37 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; 38 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 39 40 import static org.junit.Assert.assertEquals; 41 import static org.junit.Assert.assertTrue; 42 import static org.junit.Assert.fail; 43 import static org.mockito.ArgumentMatchers.argThat; 44 import static org.mockito.ArgumentMatchers.notNull; 45 import static org.mockito.Matchers.anyInt; 46 import static org.mockito.Matchers.anyString; 47 import static org.mockito.Matchers.eq; 48 import static org.mockito.Mockito.any; 49 import static org.mockito.Mockito.atLeastOnce; 50 import static org.mockito.Mockito.doThrow; 51 import static org.mockito.Mockito.mock; 52 import static org.mockito.Mockito.never; 53 import static org.mockito.Mockito.spy; 54 import static org.mockito.Mockito.timeout; 55 import static org.mockito.Mockito.times; 56 import static org.mockito.Mockito.verify; 57 import static org.mockito.Mockito.verifyNoMoreInteractions; 58 import static org.mockito.Mockito.when; 59 60 import android.content.BroadcastReceiver; 61 import android.content.ContentResolver; 62 import android.content.Context; 63 import android.content.Intent; 64 import android.content.IntentFilter; 65 import android.content.pm.ApplicationInfo; 66 import android.content.res.Resources; 67 import android.hardware.usb.UsbManager; 68 import android.net.INetd; 69 import android.net.INetworkPolicyManager; 70 import android.net.INetworkStatsService; 71 import android.net.ITetheringEventCallback; 72 import android.net.InterfaceConfiguration; 73 import android.net.IpPrefix; 74 import android.net.LinkAddress; 75 import android.net.LinkProperties; 76 import android.net.MacAddress; 77 import android.net.Network; 78 import android.net.NetworkCapabilities; 79 import android.net.NetworkInfo; 80 import android.net.NetworkState; 81 import android.net.NetworkUtils; 82 import android.net.RouteInfo; 83 import android.net.dhcp.DhcpServerCallbacks; 84 import android.net.dhcp.DhcpServingParamsParcel; 85 import android.net.dhcp.IDhcpServer; 86 import android.net.ip.IpServer; 87 import android.net.ip.RouterAdvertisementDaemon; 88 import android.net.util.InterfaceParams; 89 import android.net.util.NetworkConstants; 90 import android.net.util.SharedLog; 91 import android.net.wifi.WifiConfiguration; 92 import android.net.wifi.WifiManager; 93 import android.os.Bundle; 94 import android.os.Handler; 95 import android.os.INetworkManagementService; 96 import android.os.PersistableBundle; 97 import android.os.RemoteException; 98 import android.os.UserHandle; 99 import android.os.UserManager; 100 import android.os.test.TestLooper; 101 import android.provider.Settings; 102 import android.telephony.CarrierConfigManager; 103 import android.test.mock.MockContentResolver; 104 105 import androidx.test.filters.SmallTest; 106 import androidx.test.runner.AndroidJUnit4; 107 108 import com.android.internal.util.ArrayUtils; 109 import com.android.internal.util.StateMachine; 110 import com.android.internal.util.test.BroadcastInterceptingContext; 111 import com.android.internal.util.test.FakeSettingsProvider; 112 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; 113 import com.android.server.connectivity.tethering.OffloadHardwareInterface; 114 import com.android.server.connectivity.tethering.TetheringDependencies; 115 import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; 116 117 import org.junit.After; 118 import org.junit.Before; 119 import org.junit.Test; 120 import org.junit.runner.RunWith; 121 import org.mockito.Mock; 122 import org.mockito.MockitoAnnotations; 123 124 import java.net.Inet4Address; 125 import java.net.Inet6Address; 126 import java.util.ArrayList; 127 import java.util.Arrays; 128 import java.util.Vector; 129 130 @RunWith(AndroidJUnit4.class) 131 @SmallTest 132 public class TetheringTest { 133 private static final int IFINDEX_OFFSET = 100; 134 135 private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; 136 private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; 137 private static final String TEST_USB_IFNAME = "test_rndis0"; 138 private static final String TEST_WLAN_IFNAME = "test_wlan0"; 139 140 private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; 141 142 @Mock private ApplicationInfo mApplicationInfo; 143 @Mock private Context mContext; 144 @Mock private INetworkManagementService mNMService; 145 @Mock private INetworkStatsService mStatsService; 146 @Mock private INetworkPolicyManager mPolicyManager; 147 @Mock private MockableSystemProperties mSystemProperties; 148 @Mock private OffloadHardwareInterface mOffloadHardwareInterface; 149 @Mock private Resources mResources; 150 @Mock private UsbManager mUsbManager; 151 @Mock private WifiManager mWifiManager; 152 @Mock private CarrierConfigManager mCarrierConfigManager; 153 @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; 154 @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; 155 @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; 156 @Mock private IDhcpServer mDhcpServer; 157 @Mock private INetd mNetd; 158 159 private final MockIpServerDependencies mIpServerDependencies = 160 spy(new MockIpServerDependencies()); 161 private final MockTetheringDependencies mTetheringDependencies = 162 new MockTetheringDependencies(); 163 164 // Like so many Android system APIs, these cannot be mocked because it is marked final. 165 // We have to use the real versions. 166 private final PersistableBundle mCarrierConfig = new PersistableBundle(); 167 private final TestLooper mLooper = new TestLooper(); 168 169 private Vector<Intent> mIntents; 170 private BroadcastInterceptingContext mServiceContext; 171 private MockContentResolver mContentResolver; 172 private BroadcastReceiver mBroadcastReceiver; 173 private Tethering mTethering; 174 175 private class MockContext extends BroadcastInterceptingContext { 176 MockContext(Context base) { 177 super(base); 178 } 179 180 @Override 181 public ApplicationInfo getApplicationInfo() { return mApplicationInfo; } 182 183 @Override 184 public ContentResolver getContentResolver() { return mContentResolver; } 185 186 @Override 187 public String getPackageName() { return "TetheringTest"; } 188 189 @Override 190 public Resources getResources() { return mResources; } 191 192 @Override 193 public Object getSystemService(String name) { 194 if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; 195 if (Context.USB_SERVICE.equals(name)) return mUsbManager; 196 return super.getSystemService(name); 197 } 198 } 199 200 public class MockIpServerDependencies extends IpServer.Dependencies { 201 @Override 202 public RouterAdvertisementDaemon getRouterAdvertisementDaemon( 203 InterfaceParams ifParams) { 204 return mRouterAdvertisementDaemon; 205 } 206 207 @Override 208 public InterfaceParams getInterfaceParams(String ifName) { 209 assertTrue("Non-mocked interface " + ifName, 210 ifName.equals(TEST_USB_IFNAME) 211 || ifName.equals(TEST_WLAN_IFNAME) 212 || ifName.equals(TEST_MOBILE_IFNAME)); 213 final String[] ifaces = new String[] { 214 TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME }; 215 return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, 216 MacAddress.ALL_ZEROS_ADDRESS); 217 } 218 219 @Override 220 public INetd getNetdService() { 221 return mNetd; 222 } 223 224 @Override 225 public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, 226 DhcpServerCallbacks cb) { 227 new Thread(() -> { 228 try { 229 cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); 230 } catch (RemoteException e) { 231 fail(e.getMessage()); 232 } 233 }).run(); 234 } 235 } 236 237 public class MockTetheringDependencies extends TetheringDependencies { 238 StateMachine upstreamNetworkMonitorMasterSM; 239 ArrayList<IpServer> ipv6CoordinatorNotifyList; 240 int isTetheringSupportedCalls; 241 242 public void reset() { 243 upstreamNetworkMonitorMasterSM = null; 244 ipv6CoordinatorNotifyList = null; 245 isTetheringSupportedCalls = 0; 246 } 247 248 @Override 249 public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { 250 return mOffloadHardwareInterface; 251 } 252 253 @Override 254 public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, 255 StateMachine target, SharedLog log, int what) { 256 upstreamNetworkMonitorMasterSM = target; 257 return mUpstreamNetworkMonitor; 258 } 259 260 @Override 261 public IPv6TetheringCoordinator getIPv6TetheringCoordinator( 262 ArrayList<IpServer> notifyList, SharedLog log) { 263 ipv6CoordinatorNotifyList = notifyList; 264 return mIPv6TetheringCoordinator; 265 } 266 267 @Override 268 public IpServer.Dependencies getIpServerDependencies() { 269 return mIpServerDependencies; 270 } 271 272 @Override 273 public boolean isTetheringSupported() { 274 isTetheringSupportedCalls++; 275 return true; 276 } 277 278 @Override 279 public int getDefaultDataSubscriptionId() { 280 return INVALID_SUBSCRIPTION_ID; 281 } 282 } 283 284 private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6, 285 boolean with464xlat) { 286 final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null); 287 info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); 288 final LinkProperties prop = new LinkProperties(); 289 prop.setInterfaceName(TEST_MOBILE_IFNAME); 290 291 if (withIPv4) { 292 prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), 293 NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME)); 294 } 295 296 if (withIPv6) { 297 prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2")); 298 prop.addLinkAddress( 299 new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"), 300 NetworkConstants.RFC7421_PREFIX_LENGTH)); 301 prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), 302 NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME)); 303 } 304 305 if (with464xlat) { 306 final LinkProperties stackedLink = new LinkProperties(); 307 stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); 308 stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), 309 NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME)); 310 311 prop.addStackedLink(stackedLink); 312 } 313 314 315 final NetworkCapabilities capabilities = new NetworkCapabilities() 316 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);; 317 return new NetworkState(info, prop, capabilities, new Network(100), null, "netid"); 318 } 319 320 private static NetworkState buildMobileIPv4UpstreamState() { 321 return buildMobileUpstreamState(true, false, false); 322 } 323 324 private static NetworkState buildMobileIPv6UpstreamState() { 325 return buildMobileUpstreamState(false, true, false); 326 } 327 328 private static NetworkState buildMobileDualStackUpstreamState() { 329 return buildMobileUpstreamState(true, true, false); 330 } 331 332 private static NetworkState buildMobile464xlatUpstreamState() { 333 return buildMobileUpstreamState(false, true, true); 334 } 335 336 @Before 337 public void setUp() throws Exception { 338 MockitoAnnotations.initMocks(this); 339 when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) 340 .thenReturn(new String[0]); 341 when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) 342 .thenReturn(new String[] { "test_rndis\\d" }); 343 when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) 344 .thenReturn(new String[]{ "test_wlan\\d" }); 345 when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) 346 .thenReturn(new String[0]); 347 when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) 348 .thenReturn(new int[0]); 349 when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) 350 .thenReturn(false); 351 when(mNMService.listInterfaces()) 352 .thenReturn(new String[] { 353 TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME}); 354 when(mNMService.getInterfaceConfig(anyString())) 355 .thenReturn(new InterfaceConfiguration()); 356 when(mRouterAdvertisementDaemon.start()) 357 .thenReturn(true); 358 359 mServiceContext = new MockContext(mContext); 360 mContentResolver = new MockContentResolver(mServiceContext); 361 mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 362 Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); 363 mIntents = new Vector<>(); 364 mBroadcastReceiver = new BroadcastReceiver() { 365 @Override 366 public void onReceive(Context context, Intent intent) { 367 mIntents.addElement(intent); 368 } 369 }; 370 mServiceContext.registerReceiver(mBroadcastReceiver, 371 new IntentFilter(ACTION_TETHER_STATE_CHANGED)); 372 mTetheringDependencies.reset(); 373 mTethering = makeTethering(); 374 verify(mNMService).registerTetheringStatsProvider(any(), anyString()); 375 } 376 377 private Tethering makeTethering() { 378 return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, 379 mLooper.getLooper(), mSystemProperties, 380 mTetheringDependencies); 381 } 382 383 @After 384 public void tearDown() { 385 mServiceContext.unregisterReceiver(mBroadcastReceiver); 386 } 387 388 private void sendWifiApStateChanged(int state) { 389 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 390 intent.putExtra(EXTRA_WIFI_AP_STATE, state); 391 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 392 } 393 394 private void sendWifiApStateChanged(int state, String ifname, int ipmode) { 395 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 396 intent.putExtra(EXTRA_WIFI_AP_STATE, state); 397 intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); 398 intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); 399 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 400 } 401 402 private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) { 403 final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); 404 intent.putExtra(USB_CONNECTED, connected); 405 intent.putExtra(USB_CONFIGURED, configured); 406 intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction); 407 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 408 } 409 410 private void sendConfigurationChanged() { 411 final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); 412 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 413 } 414 415 private void verifyInterfaceServingModeStarted() throws Exception { 416 verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME); 417 verify(mNMService, times(1)) 418 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); 419 verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); 420 } 421 422 private void verifyTetheringBroadcast(String ifname, String whichExtra) { 423 // Verify that ifname is in the whichExtra array of the tether state changed broadcast. 424 final Intent bcast = mIntents.get(0); 425 assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction()); 426 final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra); 427 assertTrue(ifnames.contains(ifname)); 428 mIntents.remove(bcast); 429 } 430 431 public void failingLocalOnlyHotspotLegacyApBroadcast( 432 boolean emulateInterfaceStatusChanged) throws Exception { 433 // Emulate externally-visible WifiManager effects, causing the 434 // per-interface state machine to start up, and telling us that 435 // hotspot mode is to be started. 436 if (emulateInterfaceStatusChanged) { 437 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 438 } 439 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); 440 mLooper.dispatchAll(); 441 442 // If, and only if, Tethering received an interface status changed then 443 // it creates a IpServer and sends out a broadcast indicating that the 444 // interface is "available". 445 if (emulateInterfaceStatusChanged) { 446 assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls); 447 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); 448 } 449 verifyNoMoreInteractions(mNMService); 450 verifyNoMoreInteractions(mWifiManager); 451 } 452 453 private void prepareUsbTethering(NetworkState upstreamState) { 454 when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); 455 when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) 456 .thenReturn(upstreamState); 457 458 // Emulate pressing the USB tethering button in Settings UI. 459 mTethering.startTethering(TETHERING_USB, null, false); 460 mLooper.dispatchAll(); 461 verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); 462 463 mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); 464 } 465 466 @Test 467 public void testUsbConfiguredBroadcastStartsTethering() throws Exception { 468 NetworkState upstreamState = buildMobileIPv4UpstreamState(); 469 prepareUsbTethering(upstreamState); 470 471 // This should produce no activity of any kind. 472 verifyNoMoreInteractions(mNMService); 473 474 // Pretend we then receive USB configured broadcast. 475 sendUsbBroadcast(true, true, true); 476 mLooper.dispatchAll(); 477 // Now we should see the start of tethering mechanics (in this case: 478 // tetherMatchingInterfaces() which starts by fetching all interfaces). 479 verify(mNMService, times(1)).listInterfaces(); 480 481 // UpstreamNetworkMonitor should receive selected upstream 482 verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); 483 verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); 484 } 485 486 @Test 487 public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception { 488 failingLocalOnlyHotspotLegacyApBroadcast(true); 489 } 490 491 @Test 492 public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception { 493 failingLocalOnlyHotspotLegacyApBroadcast(false); 494 } 495 496 public void workingLocalOnlyHotspotEnrichedApBroadcast( 497 boolean emulateInterfaceStatusChanged) throws Exception { 498 // Emulate externally-visible WifiManager effects, causing the 499 // per-interface state machine to start up, and telling us that 500 // hotspot mode is to be started. 501 if (emulateInterfaceStatusChanged) { 502 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 503 } 504 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); 505 mLooper.dispatchAll(); 506 507 verifyInterfaceServingModeStarted(); 508 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); 509 verify(mNMService, times(1)).setIpForwardingEnabled(true); 510 verify(mNMService, times(1)).startTethering(any(String[].class)); 511 verifyNoMoreInteractions(mNMService); 512 verify(mWifiManager).updateInterfaceIpState( 513 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); 514 verifyNoMoreInteractions(mWifiManager); 515 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); 516 verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); 517 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). 518 assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls); 519 520 // Emulate externally-visible WifiManager effects, when hotspot mode 521 // is being torn down. 522 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); 523 mTethering.interfaceRemoved(TEST_WLAN_IFNAME); 524 mLooper.dispatchAll(); 525 526 verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); 527 // TODO: Why is {g,s}etInterfaceConfig() called more than once? 528 verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); 529 verify(mNMService, atLeastOnce()) 530 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); 531 verify(mNMService, times(1)).stopTethering(); 532 verify(mNMService, times(1)).setIpForwardingEnabled(false); 533 verifyNoMoreInteractions(mNMService); 534 verifyNoMoreInteractions(mWifiManager); 535 // Asking for the last error after the per-interface state machine 536 // has been reaped yields an unknown interface error. 537 assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); 538 } 539 540 /** 541 * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator. 542 */ 543 private void sendIPv6TetherUpdates(NetworkState upstreamState) { 544 // IPv6TetheringCoordinator must have been notified of downstream 545 verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( 546 argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), 547 eq(IpServer.STATE_TETHERED)); 548 549 for (IpServer ipSrv : 550 mTetheringDependencies.ipv6CoordinatorNotifyList) { 551 NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); 552 ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, 553 upstreamState.linkProperties.isIpv6Provisioned() 554 ? ipv6OnlyState.linkProperties 555 : null); 556 } 557 mLooper.dispatchAll(); 558 } 559 560 private void runUsbTethering(NetworkState upstreamState) { 561 prepareUsbTethering(upstreamState); 562 sendUsbBroadcast(true, true, true); 563 mLooper.dispatchAll(); 564 } 565 566 @Test 567 public void workingMobileUsbTethering_IPv4() throws Exception { 568 NetworkState upstreamState = buildMobileIPv4UpstreamState(); 569 runUsbTethering(upstreamState); 570 571 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 572 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 573 574 sendIPv6TetherUpdates(upstreamState); 575 verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); 576 verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); 577 } 578 579 @Test 580 public void workingMobileUsbTethering_IPv4LegacyDhcp() { 581 Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); 582 mTethering = makeTethering(); 583 final NetworkState upstreamState = buildMobileIPv4UpstreamState(); 584 runUsbTethering(upstreamState); 585 sendIPv6TetherUpdates(upstreamState); 586 587 verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); 588 } 589 590 @Test 591 public void workingMobileUsbTethering_IPv6() throws Exception { 592 NetworkState upstreamState = buildMobileIPv6UpstreamState(); 593 runUsbTethering(upstreamState); 594 595 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 596 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 597 598 sendIPv6TetherUpdates(upstreamState); 599 verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); 600 verify(mNetd, times(1)).tetherApplyDnsInterfaces(); 601 } 602 603 @Test 604 public void workingMobileUsbTethering_DualStack() throws Exception { 605 NetworkState upstreamState = buildMobileDualStackUpstreamState(); 606 runUsbTethering(upstreamState); 607 608 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 609 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 610 verify(mRouterAdvertisementDaemon, times(1)).start(); 611 verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); 612 613 sendIPv6TetherUpdates(upstreamState); 614 verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); 615 verify(mNetd, times(1)).tetherApplyDnsInterfaces(); 616 } 617 618 @Test 619 public void workingMobileUsbTethering_MultipleUpstreams() throws Exception { 620 NetworkState upstreamState = buildMobile464xlatUpstreamState(); 621 runUsbTethering(upstreamState); 622 623 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); 624 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 625 verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); 626 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 627 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, 628 TEST_XLAT_MOBILE_IFNAME); 629 630 sendIPv6TetherUpdates(upstreamState); 631 verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); 632 verify(mNetd, times(1)).tetherApplyDnsInterfaces(); 633 } 634 635 @Test 636 public void workingMobileUsbTethering_v6Then464xlat() throws Exception { 637 // Setup IPv6 638 NetworkState upstreamState = buildMobileIPv6UpstreamState(); 639 runUsbTethering(upstreamState); 640 641 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 642 verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); 643 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 644 645 // Then 464xlat comes up 646 upstreamState = buildMobile464xlatUpstreamState(); 647 when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) 648 .thenReturn(upstreamState); 649 650 // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. 651 mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage( 652 Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK, 653 UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, 654 0, 655 upstreamState); 656 mLooper.dispatchAll(); 657 658 // Forwarding is added for 464xlat 659 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); 660 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, 661 TEST_XLAT_MOBILE_IFNAME); 662 // Forwarding was not re-added for v6 (still times(1)) 663 verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 664 verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); 665 // DHCP not restarted on downstream (still times(1)) 666 verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); 667 } 668 669 @Test 670 public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { 671 when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) 672 .thenReturn(true); 673 sendConfigurationChanged(); 674 675 // Setup IPv6 676 final NetworkState upstreamState = buildMobileIPv6UpstreamState(); 677 runUsbTethering(upstreamState); 678 679 // UpstreamNetworkMonitor should choose upstream automatically 680 // (in this specific case: choose the default network). 681 verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); 682 verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); 683 684 verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); 685 } 686 687 @Test 688 public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { 689 workingLocalOnlyHotspotEnrichedApBroadcast(true); 690 } 691 692 @Test 693 public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception { 694 workingLocalOnlyHotspotEnrichedApBroadcast(false); 695 } 696 697 // TODO: Test with and without interfaceStatusChanged(). 698 @Test 699 public void failingWifiTetheringLegacyApBroadcast() throws Exception { 700 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 701 702 // Emulate pressing the WiFi tethering button. 703 mTethering.startTethering(TETHERING_WIFI, null, false); 704 mLooper.dispatchAll(); 705 verify(mWifiManager, times(1)).startSoftAp(null); 706 verifyNoMoreInteractions(mWifiManager); 707 verifyNoMoreInteractions(mNMService); 708 709 // Emulate externally-visible WifiManager effects, causing the 710 // per-interface state machine to start up, and telling us that 711 // tethering mode is to be started. 712 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 713 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); 714 mLooper.dispatchAll(); 715 716 assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls); 717 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); 718 verifyNoMoreInteractions(mNMService); 719 verifyNoMoreInteractions(mWifiManager); 720 } 721 722 // TODO: Test with and without interfaceStatusChanged(). 723 @Test 724 public void workingWifiTetheringEnrichedApBroadcast() throws Exception { 725 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 726 727 // Emulate pressing the WiFi tethering button. 728 mTethering.startTethering(TETHERING_WIFI, null, false); 729 mLooper.dispatchAll(); 730 verify(mWifiManager, times(1)).startSoftAp(null); 731 verifyNoMoreInteractions(mWifiManager); 732 verifyNoMoreInteractions(mNMService); 733 734 // Emulate externally-visible WifiManager effects, causing the 735 // per-interface state machine to start up, and telling us that 736 // tethering mode is to be started. 737 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 738 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); 739 mLooper.dispatchAll(); 740 741 verifyInterfaceServingModeStarted(); 742 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); 743 verify(mNMService, times(1)).setIpForwardingEnabled(true); 744 verify(mNMService, times(1)).startTethering(any(String[].class)); 745 verifyNoMoreInteractions(mNMService); 746 verify(mWifiManager).updateInterfaceIpState( 747 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); 748 verifyNoMoreInteractions(mWifiManager); 749 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER); 750 verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); 751 // In tethering mode, in the default configuration, an explicit request 752 // for a mobile network is also made. 753 verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); 754 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). 755 assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls); 756 757 ///// 758 // We do not currently emulate any upstream being found. 759 // 760 // This is why there are no calls to verify mNMService.enableNat() or 761 // mNMService.startInterfaceForwarding(). 762 ///// 763 764 // Emulate pressing the WiFi tethering button. 765 mTethering.stopTethering(TETHERING_WIFI); 766 mLooper.dispatchAll(); 767 verify(mWifiManager, times(1)).stopSoftAp(); 768 verifyNoMoreInteractions(mWifiManager); 769 verifyNoMoreInteractions(mNMService); 770 771 // Emulate externally-visible WifiManager effects, when tethering mode 772 // is being torn down. 773 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); 774 mTethering.interfaceRemoved(TEST_WLAN_IFNAME); 775 mLooper.dispatchAll(); 776 777 verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); 778 // TODO: Why is {g,s}etInterfaceConfig() called more than once? 779 verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); 780 verify(mNMService, atLeastOnce()) 781 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); 782 verify(mNMService, times(1)).stopTethering(); 783 verify(mNMService, times(1)).setIpForwardingEnabled(false); 784 verifyNoMoreInteractions(mNMService); 785 verifyNoMoreInteractions(mWifiManager); 786 // Asking for the last error after the per-interface state machine 787 // has been reaped yields an unknown interface error. 788 assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); 789 } 790 791 // TODO: Test with and without interfaceStatusChanged(). 792 @Test 793 public void failureEnablingIpForwarding() throws Exception { 794 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 795 doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); 796 797 // Emulate pressing the WiFi tethering button. 798 mTethering.startTethering(TETHERING_WIFI, null, false); 799 mLooper.dispatchAll(); 800 verify(mWifiManager, times(1)).startSoftAp(null); 801 verifyNoMoreInteractions(mWifiManager); 802 verifyNoMoreInteractions(mNMService); 803 804 // Emulate externally-visible WifiManager effects, causing the 805 // per-interface state machine to start up, and telling us that 806 // tethering mode is to be started. 807 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 808 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); 809 mLooper.dispatchAll(); 810 811 // We verify get/set called thrice here: twice for setup (on NMService) and once during 812 // teardown (on Netd) because all events happen over the course of the single 813 // dispatchAll() above. Note that once the IpServer IPv4 address config 814 // code is refactored the two calls during shutdown will revert to one. 815 verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); 816 verify(mNMService, times(2)) 817 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); 818 verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); 819 verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); 820 verify(mWifiManager).updateInterfaceIpState( 821 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); 822 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). 823 assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls); 824 verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); 825 // This is called, but will throw. 826 verify(mNMService, times(1)).setIpForwardingEnabled(true); 827 // This never gets called because of the exception thrown above. 828 verify(mNMService, times(0)).startTethering(any(String[].class)); 829 // When the master state machine transitions to an error state it tells 830 // downstream interfaces, which causes us to tell Wi-Fi about the error 831 // so it can take down AP mode. 832 verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); 833 verify(mWifiManager).updateInterfaceIpState( 834 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); 835 836 verifyNoMoreInteractions(mWifiManager); 837 verifyNoMoreInteractions(mNMService); 838 } 839 840 private void userRestrictionsListenerBehaviour( 841 boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, 842 int expectedInteractionsWithShowNotification) throws Exception { 843 final int userId = 0; 844 final Bundle currRestrictions = new Bundle(); 845 final Bundle newRestrictions = new Bundle(); 846 Tethering tethering = mock(Tethering.class); 847 Tethering.TetheringUserRestrictionListener turl = 848 new Tethering.TetheringUserRestrictionListener(tethering); 849 850 currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow); 851 newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); 852 when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); 853 854 turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions); 855 856 verify(tethering, times(expectedInteractionsWithShowNotification)) 857 .showTetheredNotification(anyInt(), eq(false)); 858 859 verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll(); 860 } 861 862 @Test 863 public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { 864 final String[] emptyActiveIfacesList = new String[]{}; 865 final boolean currDisallow = false; 866 final boolean nextDisallow = true; 867 final int expectedInteractionsWithShowNotification = 0; 868 869 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList, 870 expectedInteractionsWithShowNotification); 871 } 872 873 @Test 874 public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { 875 final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; 876 final boolean currDisallow = false; 877 final boolean nextDisallow = true; 878 final int expectedInteractionsWithShowNotification = 1; 879 880 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, 881 expectedInteractionsWithShowNotification); 882 } 883 884 @Test 885 public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { 886 final String[] nonEmptyActiveIfacesList = new String[]{}; 887 final boolean currDisallow = true; 888 final boolean nextDisallow = false; 889 final int expectedInteractionsWithShowNotification = 0; 890 891 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, 892 expectedInteractionsWithShowNotification); 893 } 894 895 @Test 896 public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { 897 final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; 898 final boolean currDisallow = true; 899 final boolean nextDisallow = false; 900 final int expectedInteractionsWithShowNotification = 0; 901 902 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, 903 expectedInteractionsWithShowNotification); 904 } 905 906 @Test 907 public void testDisallowTetheringUnchanged() throws Exception { 908 final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; 909 final int expectedInteractionsWithShowNotification = 0; 910 boolean currDisallow = true; 911 boolean nextDisallow = true; 912 913 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, 914 expectedInteractionsWithShowNotification); 915 916 currDisallow = false; 917 nextDisallow = false; 918 919 userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, 920 expectedInteractionsWithShowNotification); 921 } 922 923 private class TestTetheringEventCallback extends ITetheringEventCallback.Stub { 924 private final ArrayList<Network> mActualUpstreams = new ArrayList<>(); 925 926 public void expectUpstreamChanged(Network... networks) { 927 final ArrayList<Network> expectedUpstreams = 928 new ArrayList<Network>(Arrays.asList(networks)); 929 for (Network upstream : expectedUpstreams) { 930 // throws OOB if no expectations 931 assertEquals(mActualUpstreams.remove(0), upstream); 932 } 933 assertNoCallback(); 934 } 935 936 @Override 937 public void onUpstreamChanged(Network network) { 938 mActualUpstreams.add(network); 939 } 940 941 public void assertNoCallback() { 942 assertTrue(mActualUpstreams.isEmpty()); 943 } 944 } 945 946 @Test 947 public void testRegisterTetheringEventCallback() throws Exception { 948 TestTetheringEventCallback callback1 = new TestTetheringEventCallback(); 949 TestTetheringEventCallback callback2 = new TestTetheringEventCallback(); 950 951 // 1. Register one callback and run usb tethering. 952 mTethering.registerTetheringEventCallback(callback1); 953 mLooper.dispatchAll(); 954 callback1.expectUpstreamChanged(new Network[] {null}); 955 NetworkState upstreamState = buildMobileDualStackUpstreamState(); 956 runUsbTethering(upstreamState); 957 callback1.expectUpstreamChanged(upstreamState.network); 958 // 2. Register second callback. 959 mTethering.registerTetheringEventCallback(callback2); 960 mLooper.dispatchAll(); 961 callback2.expectUpstreamChanged(upstreamState.network); 962 // 3. Disable usb tethering. 963 mTethering.stopTethering(TETHERING_USB); 964 mLooper.dispatchAll(); 965 sendUsbBroadcast(false, false, false); 966 mLooper.dispatchAll(); 967 callback1.expectUpstreamChanged(new Network[] {null}); 968 callback2.expectUpstreamChanged(new Network[] {null}); 969 // 4. Unregister first callback and run hotspot. 970 mTethering.unregisterTetheringEventCallback(callback1); 971 mLooper.dispatchAll(); 972 when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); 973 when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) 974 .thenReturn(upstreamState); 975 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 976 mTethering.startTethering(TETHERING_WIFI, null, false); 977 mLooper.dispatchAll(); 978 mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); 979 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); 980 mLooper.dispatchAll(); 981 callback1.assertNoCallback(); 982 callback2.expectUpstreamChanged(upstreamState.network); 983 } 984 985 // TODO: Test that a request for hotspot mode doesn't interfere with an 986 // already operating tethering mode interface. 987 } 988