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