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.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; 20 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; 21 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; 22 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; 23 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; 24 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.Matchers.anyBoolean; 28 import static org.mockito.Matchers.anyInt; 29 import static org.mockito.Matchers.anyString; 30 import static org.mockito.Matchers.eq; 31 import static org.mockito.Mockito.any; 32 import static org.mockito.Mockito.atLeastOnce; 33 import static org.mockito.Mockito.doThrow; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.verifyNoMoreInteractions; 37 import static org.mockito.Mockito.when; 38 39 import android.content.BroadcastReceiver; 40 import android.content.Context; 41 import android.content.ContextWrapper; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.res.Resources; 45 import android.hardware.usb.UsbManager; 46 import android.net.ConnectivityManager; 47 import android.net.ConnectivityManager.NetworkCallback; 48 import android.net.INetworkPolicyManager; 49 import android.net.INetworkStatsService; 50 import android.net.InterfaceConfiguration; 51 import android.net.NetworkRequest; 52 import android.net.wifi.WifiConfiguration; 53 import android.net.wifi.WifiManager; 54 import android.os.Handler; 55 import android.os.INetworkManagementService; 56 import android.os.PersistableBundle; 57 import android.os.RemoteException; 58 import android.os.test.TestLooper; 59 import android.os.UserHandle; 60 import android.support.test.filters.SmallTest; 61 import android.support.test.runner.AndroidJUnit4; 62 import android.telephony.CarrierConfigManager; 63 64 import com.android.internal.util.test.BroadcastInterceptingContext; 65 66 import org.junit.After; 67 import org.junit.Before; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 import org.mockito.Mock; 71 import org.mockito.MockitoAnnotations; 72 73 import java.util.ArrayList; 74 import java.util.Vector; 75 76 @RunWith(AndroidJUnit4.class) 77 @SmallTest 78 public class TetheringTest { 79 private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; 80 81 @Mock private Context mContext; 82 @Mock private ConnectivityManager mConnectivityManager; 83 @Mock private INetworkManagementService mNMService; 84 @Mock private INetworkStatsService mStatsService; 85 @Mock private INetworkPolicyManager mPolicyManager; 86 @Mock private MockableSystemProperties mSystemProperties; 87 @Mock private Resources mResources; 88 @Mock private UsbManager mUsbManager; 89 @Mock private WifiManager mWifiManager; 90 @Mock private CarrierConfigManager mCarrierConfigManager; 91 92 // Like so many Android system APIs, these cannot be mocked because it is marked final. 93 // We have to use the real versions. 94 private final PersistableBundle mCarrierConfig = new PersistableBundle(); 95 private final TestLooper mLooper = new TestLooper(); 96 private final String mTestIfname = "test_wlan0"; 97 98 private Vector<Intent> mIntents; 99 private BroadcastInterceptingContext mServiceContext; 100 private BroadcastReceiver mBroadcastReceiver; 101 private Tethering mTethering; 102 103 private class MockContext extends BroadcastInterceptingContext { MockContext(Context base)104 MockContext(Context base) { 105 super(base); 106 } 107 108 @Override getResources()109 public Resources getResources() { return mResources; } 110 111 @Override getSystemService(String name)112 public Object getSystemService(String name) { 113 if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager; 114 if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; 115 return super.getSystemService(name); 116 } 117 } 118 119 @Before setUp()120 public void setUp() throws Exception { 121 MockitoAnnotations.initMocks(this); 122 when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) 123 .thenReturn(new String[0]); 124 when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) 125 .thenReturn(new String[0]); 126 when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) 127 .thenReturn(new String[]{ "test_wlan\\d" }); 128 when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) 129 .thenReturn(new String[0]); 130 when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) 131 .thenReturn(new int[0]); 132 when(mNMService.listInterfaces()) 133 .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname }); 134 when(mNMService.getInterfaceConfig(anyString())) 135 .thenReturn(new InterfaceConfiguration()); 136 137 mServiceContext = new MockContext(mContext); 138 mIntents = new Vector<>(); 139 mBroadcastReceiver = new BroadcastReceiver() { 140 @Override 141 public void onReceive(Context context, Intent intent) { 142 mIntents.addElement(intent); 143 } 144 }; 145 mServiceContext.registerReceiver(mBroadcastReceiver, 146 new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); 147 mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, 148 mLooper.getLooper(), mSystemProperties); 149 } 150 151 @After tearDown()152 public void tearDown() { 153 mServiceContext.unregisterReceiver(mBroadcastReceiver); 154 } 155 setupForRequiredProvisioning()156 private void setupForRequiredProvisioning() { 157 // Produce some acceptable looking provision app setting if requested. 158 when(mResources.getStringArray( 159 com.android.internal.R.array.config_mobile_hotspot_provision_app)) 160 .thenReturn(PROVISIONING_APP_NAME); 161 // Don't disable tethering provisioning unless requested. 162 when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY), 163 anyBoolean())).thenReturn(false); 164 // Act like the CarrierConfigManager is present and ready unless told otherwise. 165 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) 166 .thenReturn(mCarrierConfigManager); 167 when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig); 168 mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); 169 } 170 171 @Test canRequireProvisioning()172 public void canRequireProvisioning() { 173 setupForRequiredProvisioning(); 174 assertTrue(mTethering.isTetherProvisioningRequired()); 175 } 176 177 @Test toleratesCarrierConfigManagerMissing()178 public void toleratesCarrierConfigManagerMissing() { 179 setupForRequiredProvisioning(); 180 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) 181 .thenReturn(null); 182 // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. 183 // We therefore still require provisioning. 184 assertTrue(mTethering.isTetherProvisioningRequired()); 185 } 186 187 @Test toleratesCarrierConfigMissing()188 public void toleratesCarrierConfigMissing() { 189 setupForRequiredProvisioning(); 190 when(mCarrierConfigManager.getConfig()).thenReturn(null); 191 // We still have a provisioning app configured, so still require provisioning. 192 assertTrue(mTethering.isTetherProvisioningRequired()); 193 } 194 195 @Test provisioningNotRequiredWhenAppNotFound()196 public void provisioningNotRequiredWhenAppNotFound() { 197 setupForRequiredProvisioning(); 198 when(mResources.getStringArray( 199 com.android.internal.R.array.config_mobile_hotspot_provision_app)) 200 .thenReturn(null); 201 assertTrue(!mTethering.isTetherProvisioningRequired()); 202 when(mResources.getStringArray( 203 com.android.internal.R.array.config_mobile_hotspot_provision_app)) 204 .thenReturn(new String[] {"malformedApp"}); 205 assertTrue(!mTethering.isTetherProvisioningRequired()); 206 } 207 sendWifiApStateChanged(int state)208 private void sendWifiApStateChanged(int state) { 209 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 210 intent.putExtra(EXTRA_WIFI_AP_STATE, state); 211 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 212 } 213 sendWifiApStateChanged(int state, String ifname, int ipmode)214 private void sendWifiApStateChanged(int state, String ifname, int ipmode) { 215 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 216 intent.putExtra(EXTRA_WIFI_AP_STATE, state); 217 intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); 218 intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); 219 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 220 } 221 verifyInterfaceServingModeStarted()222 private void verifyInterfaceServingModeStarted() throws Exception { 223 verify(mNMService, times(1)).getInterfaceConfig(mTestIfname); 224 verify(mNMService, times(1)) 225 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); 226 verify(mNMService, times(1)).tetherInterface(mTestIfname); 227 } 228 verifyTetheringBroadcast(String ifname, String whichExtra)229 private void verifyTetheringBroadcast(String ifname, String whichExtra) { 230 // Verify that ifname is in the whichExtra array of the tether state changed broadcast. 231 final Intent bcast = mIntents.get(0); 232 assertEquals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, bcast.getAction()); 233 final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra); 234 assertTrue(ifnames.contains(ifname)); 235 mIntents.remove(bcast); 236 } 237 failingLocalOnlyHotspotLegacyApBroadcast( boolean emulateInterfaceStatusChanged)238 public void failingLocalOnlyHotspotLegacyApBroadcast( 239 boolean emulateInterfaceStatusChanged) throws Exception { 240 when(mConnectivityManager.isTetheringSupported()).thenReturn(true); 241 242 // Emulate externally-visible WifiManager effects, causing the 243 // per-interface state machine to start up, and telling us that 244 // hotspot mode is to be started. 245 if (emulateInterfaceStatusChanged) { 246 mTethering.interfaceStatusChanged(mTestIfname, true); 247 } 248 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); 249 mLooper.dispatchAll(); 250 251 // If, and only if, Tethering received an interface status changed 252 // then it creates a TetherInterfaceStateMachine and sends out a 253 // broadcast indicating that the interface is "available". 254 if (emulateInterfaceStatusChanged) { 255 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); 256 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); 257 } 258 verifyNoMoreInteractions(mConnectivityManager); 259 verifyNoMoreInteractions(mNMService); 260 verifyNoMoreInteractions(mWifiManager); 261 } 262 263 @Test failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged()264 public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception { 265 failingLocalOnlyHotspotLegacyApBroadcast(true); 266 } 267 268 @Test failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged()269 public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception { 270 failingLocalOnlyHotspotLegacyApBroadcast(false); 271 } 272 workingLocalOnlyHotspotEnrichedApBroadcast( boolean emulateInterfaceStatusChanged)273 public void workingLocalOnlyHotspotEnrichedApBroadcast( 274 boolean emulateInterfaceStatusChanged) throws Exception { 275 when(mConnectivityManager.isTetheringSupported()).thenReturn(true); 276 277 // Emulate externally-visible WifiManager effects, causing the 278 // per-interface state machine to start up, and telling us that 279 // hotspot mode is to be started. 280 if (emulateInterfaceStatusChanged) { 281 mTethering.interfaceStatusChanged(mTestIfname, true); 282 } 283 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY); 284 mLooper.dispatchAll(); 285 286 verifyInterfaceServingModeStarted(); 287 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); 288 verify(mNMService, times(1)).setIpForwardingEnabled(true); 289 verify(mNMService, times(1)).startTethering(any(String[].class)); 290 verifyNoMoreInteractions(mNMService); 291 verify(mWifiManager).updateInterfaceIpState( 292 mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); 293 verifyNoMoreInteractions(mWifiManager); 294 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY); 295 // UpstreamNetworkMonitor will be started, and will register two callbacks: 296 // a "listen all" and a "track default". 297 verify(mConnectivityManager, times(1)).registerNetworkCallback( 298 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); 299 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback( 300 any(NetworkCallback.class), any(Handler.class)); 301 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). 302 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); 303 verifyNoMoreInteractions(mConnectivityManager); 304 305 // Emulate externally-visible WifiManager effects, when hotspot mode 306 // is being torn down. 307 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); 308 mTethering.interfaceRemoved(mTestIfname); 309 mLooper.dispatchAll(); 310 311 verify(mNMService, times(1)).untetherInterface(mTestIfname); 312 // TODO: Why is {g,s}etInterfaceConfig() called more than once? 313 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname); 314 verify(mNMService, atLeastOnce()) 315 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); 316 verify(mNMService, times(1)).stopTethering(); 317 verify(mNMService, times(1)).setIpForwardingEnabled(false); 318 verifyNoMoreInteractions(mNMService); 319 verifyNoMoreInteractions(mWifiManager); 320 // Asking for the last error after the per-interface state machine 321 // has been reaped yields an unknown interface error. 322 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE, 323 mTethering.getLastTetherError(mTestIfname)); 324 } 325 326 @Test workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged()327 public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { 328 workingLocalOnlyHotspotEnrichedApBroadcast(true); 329 } 330 331 @Test workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged()332 public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception { 333 workingLocalOnlyHotspotEnrichedApBroadcast(false); 334 } 335 336 // TODO: Test with and without interfaceStatusChanged(). 337 @Test failingWifiTetheringLegacyApBroadcast()338 public void failingWifiTetheringLegacyApBroadcast() throws Exception { 339 when(mConnectivityManager.isTetheringSupported()).thenReturn(true); 340 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 341 342 // Emulate pressing the WiFi tethering button. 343 mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); 344 mLooper.dispatchAll(); 345 verify(mWifiManager, times(1)).startSoftAp(null); 346 verifyNoMoreInteractions(mWifiManager); 347 verifyNoMoreInteractions(mConnectivityManager); 348 verifyNoMoreInteractions(mNMService); 349 350 // Emulate externally-visible WifiManager effects, causing the 351 // per-interface state machine to start up, and telling us that 352 // tethering mode is to be started. 353 mTethering.interfaceStatusChanged(mTestIfname, true); 354 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); 355 mLooper.dispatchAll(); 356 357 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); 358 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); 359 verifyNoMoreInteractions(mConnectivityManager); 360 verifyNoMoreInteractions(mNMService); 361 verifyNoMoreInteractions(mWifiManager); 362 } 363 364 // TODO: Test with and without interfaceStatusChanged(). 365 @Test workingWifiTetheringEnrichedApBroadcast()366 public void workingWifiTetheringEnrichedApBroadcast() throws Exception { 367 when(mConnectivityManager.isTetheringSupported()).thenReturn(true); 368 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 369 370 // Emulate pressing the WiFi tethering button. 371 mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); 372 mLooper.dispatchAll(); 373 verify(mWifiManager, times(1)).startSoftAp(null); 374 verifyNoMoreInteractions(mWifiManager); 375 verifyNoMoreInteractions(mConnectivityManager); 376 verifyNoMoreInteractions(mNMService); 377 378 // Emulate externally-visible WifiManager effects, causing the 379 // per-interface state machine to start up, and telling us that 380 // tethering mode is to be started. 381 mTethering.interfaceStatusChanged(mTestIfname, true); 382 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); 383 mLooper.dispatchAll(); 384 385 verifyInterfaceServingModeStarted(); 386 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); 387 verify(mNMService, times(1)).setIpForwardingEnabled(true); 388 verify(mNMService, times(1)).startTethering(any(String[].class)); 389 verifyNoMoreInteractions(mNMService); 390 verify(mWifiManager).updateInterfaceIpState( 391 mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); 392 verifyNoMoreInteractions(mWifiManager); 393 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER); 394 // UpstreamNetworkMonitor will be started, and will register two callbacks: 395 // a "listen all" and a "track default". 396 verify(mConnectivityManager, times(1)).registerNetworkCallback( 397 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); 398 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback( 399 any(NetworkCallback.class), any(Handler.class)); 400 // In tethering mode, in the default configuration, an explicit request 401 // for a mobile network is also made. 402 verify(mConnectivityManager, atLeastOnce()).getNetworkInfo(anyInt()); 403 verify(mConnectivityManager, times(1)).requestNetwork( 404 any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(), 405 any(Handler.class)); 406 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). 407 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); 408 verifyNoMoreInteractions(mConnectivityManager); 409 410 ///// 411 // We do not currently emulate any upstream being found. 412 // 413 // This is why there are no calls to verify mNMService.enableNat() or 414 // mNMService.startInterfaceForwarding(). 415 ///// 416 417 // Emulate pressing the WiFi tethering button. 418 mTethering.stopTethering(ConnectivityManager.TETHERING_WIFI); 419 mLooper.dispatchAll(); 420 verify(mWifiManager, times(1)).stopSoftAp(); 421 verifyNoMoreInteractions(mWifiManager); 422 verifyNoMoreInteractions(mConnectivityManager); 423 verifyNoMoreInteractions(mNMService); 424 425 // Emulate externally-visible WifiManager effects, when tethering mode 426 // is being torn down. 427 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); 428 mTethering.interfaceRemoved(mTestIfname); 429 mLooper.dispatchAll(); 430 431 verify(mNMService, times(1)).untetherInterface(mTestIfname); 432 // TODO: Why is {g,s}etInterfaceConfig() called more than once? 433 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname); 434 verify(mNMService, atLeastOnce()) 435 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); 436 verify(mNMService, times(1)).stopTethering(); 437 verify(mNMService, times(1)).setIpForwardingEnabled(false); 438 verifyNoMoreInteractions(mNMService); 439 verifyNoMoreInteractions(mWifiManager); 440 // Asking for the last error after the per-interface state machine 441 // has been reaped yields an unknown interface error. 442 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE, 443 mTethering.getLastTetherError(mTestIfname)); 444 } 445 446 // TODO: Test with and without interfaceStatusChanged(). 447 @Test failureEnablingIpForwarding()448 public void failureEnablingIpForwarding() throws Exception { 449 when(mConnectivityManager.isTetheringSupported()).thenReturn(true); 450 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); 451 doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); 452 453 // Emulate pressing the WiFi tethering button. 454 mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); 455 mLooper.dispatchAll(); 456 verify(mWifiManager, times(1)).startSoftAp(null); 457 verifyNoMoreInteractions(mWifiManager); 458 verifyNoMoreInteractions(mConnectivityManager); 459 verifyNoMoreInteractions(mNMService); 460 461 // Emulate externally-visible WifiManager effects, causing the 462 // per-interface state machine to start up, and telling us that 463 // tethering mode is to be started. 464 mTethering.interfaceStatusChanged(mTestIfname, true); 465 sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); 466 mLooper.dispatchAll(); 467 468 // We verify get/set called twice here: once for setup and once during 469 // teardown because all events happen over the course of the single 470 // dispatchAll() above. 471 verify(mNMService, times(2)).getInterfaceConfig(mTestIfname); 472 verify(mNMService, times(2)) 473 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); 474 verify(mNMService, times(1)).tetherInterface(mTestIfname); 475 verify(mWifiManager).updateInterfaceIpState( 476 mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); 477 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); 478 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); 479 // This is called, but will throw. 480 verify(mNMService, times(1)).setIpForwardingEnabled(true); 481 // This never gets called because of the exception thrown above. 482 verify(mNMService, times(0)).startTethering(any(String[].class)); 483 // When the master state machine transitions to an error state it tells 484 // downstream interfaces, which causes us to tell Wi-Fi about the error 485 // so it can take down AP mode. 486 verify(mNMService, times(1)).untetherInterface(mTestIfname); 487 verify(mWifiManager).updateInterfaceIpState( 488 mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); 489 490 verifyNoMoreInteractions(mWifiManager); 491 verifyNoMoreInteractions(mConnectivityManager); 492 verifyNoMoreInteractions(mNMService); 493 } 494 495 // TODO: Test that a request for hotspot mode doesn't interfere with an 496 // already operating tethering mode interface. 497 } 498