blob: bc89c0f4f71f1672efe19c7f4a944800e9741930 [file] [log] [blame]
Christopher Wiley497c1472016-10-11 13:26:03 -07001/*
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
Erik Klineea9cc482017-03-10 19:35:34 +090019import static org.junit.Assert.assertEquals;
Christopher Wiley497c1472016-10-11 13:26:03 -070020import static org.junit.Assert.assertTrue;
21import static org.mockito.Matchers.anyBoolean;
Erik Klineea9cc482017-03-10 19:35:34 +090022import static org.mockito.Matchers.anyInt;
23import static org.mockito.Matchers.anyString;
Christopher Wiley497c1472016-10-11 13:26:03 -070024import static org.mockito.Matchers.eq;
Erik Kline1fdc2e22017-05-08 17:56:35 +090025import static org.mockito.Mockito.any;
Erik Klineea9cc482017-03-10 19:35:34 +090026import static org.mockito.Mockito.atLeastOnce;
Erik Kline1fdc2e22017-05-08 17:56:35 +090027import static org.mockito.Mockito.doThrow;
Erik Klineea9cc482017-03-10 19:35:34 +090028import static org.mockito.Mockito.times;
29import static org.mockito.Mockito.verify;
30import static org.mockito.Mockito.verifyNoMoreInteractions;
Christopher Wiley497c1472016-10-11 13:26:03 -070031import static org.mockito.Mockito.when;
32
Erik Kline8351faa2017-04-17 16:47:23 +090033import android.content.BroadcastReceiver;
Christopher Wiley497c1472016-10-11 13:26:03 -070034import android.content.Context;
Erik Klineea9cc482017-03-10 19:35:34 +090035import android.content.ContextWrapper;
36import android.content.Intent;
Erik Kline8351faa2017-04-17 16:47:23 +090037import android.content.IntentFilter;
Christopher Wiley497c1472016-10-11 13:26:03 -070038import android.content.res.Resources;
Erik Klineea9cc482017-03-10 19:35:34 +090039import android.hardware.usb.UsbManager;
40import android.net.ConnectivityManager;
41import android.net.ConnectivityManager.NetworkCallback;
Christopher Wiley497c1472016-10-11 13:26:03 -070042import android.net.INetworkPolicyManager;
43import android.net.INetworkStatsService;
Erik Klineea9cc482017-03-10 19:35:34 +090044import android.net.InterfaceConfiguration;
45import android.net.NetworkRequest;
46import android.net.wifi.WifiConfiguration;
47import android.net.wifi.WifiManager;
48import android.os.Handler;
Christopher Wiley497c1472016-10-11 13:26:03 -070049import android.os.INetworkManagementService;
50import android.os.PersistableBundle;
Erik Kline1fdc2e22017-05-08 17:56:35 +090051import android.os.RemoteException;
Christopher Wiley497c1472016-10-11 13:26:03 -070052import android.os.test.TestLooper;
Erik Klineea9cc482017-03-10 19:35:34 +090053import android.os.UserHandle;
Christopher Wiley497c1472016-10-11 13:26:03 -070054import android.support.test.filters.SmallTest;
55import android.support.test.runner.AndroidJUnit4;
56import android.telephony.CarrierConfigManager;
57
Erik Klineea9cc482017-03-10 19:35:34 +090058import com.android.internal.util.test.BroadcastInterceptingContext;
Erik Kline5a7c8a02017-04-30 19:36:15 +090059import com.android.server.connectivity.tethering.OffloadHardwareInterface;
60import com.android.server.connectivity.tethering.TetheringDependencies;
Erik Klineea9cc482017-03-10 19:35:34 +090061
Erik Kline8351faa2017-04-17 16:47:23 +090062import org.junit.After;
Christopher Wiley497c1472016-10-11 13:26:03 -070063import org.junit.Before;
64import org.junit.Test;
65import org.junit.runner.RunWith;
66import org.mockito.Mock;
67import org.mockito.MockitoAnnotations;
68
Erik Kline8351faa2017-04-17 16:47:23 +090069import java.util.ArrayList;
70import java.util.Vector;
71
Christopher Wiley497c1472016-10-11 13:26:03 -070072@RunWith(AndroidJUnit4.class)
73@SmallTest
74public class TetheringTest {
75 private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
76
77 @Mock private Context mContext;
Erik Klineea9cc482017-03-10 19:35:34 +090078 @Mock private ConnectivityManager mConnectivityManager;
Christopher Wiley497c1472016-10-11 13:26:03 -070079 @Mock private INetworkManagementService mNMService;
80 @Mock private INetworkStatsService mStatsService;
81 @Mock private INetworkPolicyManager mPolicyManager;
82 @Mock private MockableSystemProperties mSystemProperties;
Erik Kline5a7c8a02017-04-30 19:36:15 +090083 @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
Christopher Wiley497c1472016-10-11 13:26:03 -070084 @Mock private Resources mResources;
Erik Kline5a7c8a02017-04-30 19:36:15 +090085 @Mock private TetheringDependencies mTetheringDependencies;
Erik Klineea9cc482017-03-10 19:35:34 +090086 @Mock private UsbManager mUsbManager;
87 @Mock private WifiManager mWifiManager;
Christopher Wiley497c1472016-10-11 13:26:03 -070088 @Mock private CarrierConfigManager mCarrierConfigManager;
89
90 // Like so many Android system APIs, these cannot be mocked because it is marked final.
91 // We have to use the real versions.
92 private final PersistableBundle mCarrierConfig = new PersistableBundle();
93 private final TestLooper mLooper = new TestLooper();
Erik Klineea9cc482017-03-10 19:35:34 +090094 private final String mTestIfname = "test_wlan0";
Christopher Wiley497c1472016-10-11 13:26:03 -070095
Erik Kline8351faa2017-04-17 16:47:23 +090096 private Vector<Intent> mIntents;
Erik Klineea9cc482017-03-10 19:35:34 +090097 private BroadcastInterceptingContext mServiceContext;
Erik Kline8351faa2017-04-17 16:47:23 +090098 private BroadcastReceiver mBroadcastReceiver;
Christopher Wiley497c1472016-10-11 13:26:03 -070099 private Tethering mTethering;
100
Erik Klineea9cc482017-03-10 19:35:34 +0900101 private class MockContext extends BroadcastInterceptingContext {
102 MockContext(Context base) {
103 super(base);
104 }
105
106 @Override
107 public Resources getResources() { return mResources; }
108
109 @Override
110 public Object getSystemService(String name) {
111 if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
112 if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
113 return super.getSystemService(name);
114 }
115 }
116
Erik Kline8351faa2017-04-17 16:47:23 +0900117 @Before
118 public void setUp() throws Exception {
Christopher Wiley497c1472016-10-11 13:26:03 -0700119 MockitoAnnotations.initMocks(this);
Christopher Wiley497c1472016-10-11 13:26:03 -0700120 when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
121 .thenReturn(new String[0]);
122 when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
123 .thenReturn(new String[0]);
124 when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
Erik Klineea9cc482017-03-10 19:35:34 +0900125 .thenReturn(new String[]{ "test_wlan\\d" });
Christopher Wiley497c1472016-10-11 13:26:03 -0700126 when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
127 .thenReturn(new String[0]);
128 when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
129 .thenReturn(new int[0]);
Erik Klineea9cc482017-03-10 19:35:34 +0900130 when(mNMService.listInterfaces())
131 .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
132 when(mNMService.getInterfaceConfig(anyString()))
133 .thenReturn(new InterfaceConfiguration());
134
135 mServiceContext = new MockContext(mContext);
Erik Kline8351faa2017-04-17 16:47:23 +0900136 mIntents = new Vector<>();
137 mBroadcastReceiver = new BroadcastReceiver() {
138 @Override
139 public void onReceive(Context context, Intent intent) {
140 mIntents.addElement(intent);
141 }
142 };
143 mServiceContext.registerReceiver(mBroadcastReceiver,
144 new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
Erik Kline5a7c8a02017-04-30 19:36:15 +0900145 when(mTetheringDependencies.getOffloadHardwareInterface())
146 .thenReturn(mOffloadHardwareInterface);
Erik Klineea9cc482017-03-10 19:35:34 +0900147 mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
Erik Kline5a7c8a02017-04-30 19:36:15 +0900148 mLooper.getLooper(), mSystemProperties,
149 mTetheringDependencies);
Christopher Wiley497c1472016-10-11 13:26:03 -0700150 }
151
Erik Kline8351faa2017-04-17 16:47:23 +0900152 @After
153 public void tearDown() {
154 mServiceContext.unregisterReceiver(mBroadcastReceiver);
155 }
156
Christopher Wiley497c1472016-10-11 13:26:03 -0700157 private void setupForRequiredProvisioning() {
158 // Produce some acceptable looking provision app setting if requested.
159 when(mResources.getStringArray(
160 com.android.internal.R.array.config_mobile_hotspot_provision_app))
161 .thenReturn(PROVISIONING_APP_NAME);
162 // Don't disable tethering provisioning unless requested.
163 when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
164 anyBoolean())).thenReturn(false);
165 // Act like the CarrierConfigManager is present and ready unless told otherwise.
166 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
167 .thenReturn(mCarrierConfigManager);
168 when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
169 mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
170 }
171
172 @Test
173 public void canRequireProvisioning() {
174 setupForRequiredProvisioning();
175 assertTrue(mTethering.isTetherProvisioningRequired());
176 }
177
178 @Test
179 public void toleratesCarrierConfigManagerMissing() {
180 setupForRequiredProvisioning();
181 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
182 .thenReturn(null);
183 // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
184 // We therefore still require provisioning.
185 assertTrue(mTethering.isTetherProvisioningRequired());
186 }
187
188 @Test
189 public void toleratesCarrierConfigMissing() {
190 setupForRequiredProvisioning();
191 when(mCarrierConfigManager.getConfig()).thenReturn(null);
192 // We still have a provisioning app configured, so still require provisioning.
193 assertTrue(mTethering.isTetherProvisioningRequired());
194 }
195
196 @Test
197 public void provisioningNotRequiredWhenAppNotFound() {
198 setupForRequiredProvisioning();
199 when(mResources.getStringArray(
200 com.android.internal.R.array.config_mobile_hotspot_provision_app))
201 .thenReturn(null);
202 assertTrue(!mTethering.isTetherProvisioningRequired());
203 when(mResources.getStringArray(
204 com.android.internal.R.array.config_mobile_hotspot_provision_app))
205 .thenReturn(new String[] {"malformedApp"});
206 assertTrue(!mTethering.isTetherProvisioningRequired());
207 }
Erik Klineea9cc482017-03-10 19:35:34 +0900208
209 private void sendWifiApStateChanged(int state) {
210 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
211 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state);
212 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
213 }
214
Erik Kline8351faa2017-04-17 16:47:23 +0900215 private void verifyInterfaceServingModeStarted() throws Exception {
216 verify(mNMService, times(1)).listInterfaces();
217 verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
218 verify(mNMService, times(1))
219 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
220 verify(mNMService, times(1)).tetherInterface(mTestIfname);
221 }
222
223 private void verifyTetheringBroadcast(String ifname, String whichExtra) {
224 // Verify that ifname is in the whichExtra array of the tether state changed broadcast.
225 final Intent bcast = mIntents.get(0);
226 assertEquals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, bcast.getAction());
227 final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra);
228 assertTrue(ifnames.contains(ifname));
229 mIntents.remove(bcast);
230 }
231
Erik Klineea9cc482017-03-10 19:35:34 +0900232 @Test
233 public void workingLocalOnlyHotspot() throws Exception {
234 when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
Erik Klineea9cc482017-03-10 19:35:34 +0900235
236 // Emulate externally-visible WifiManager effects, causing the
237 // per-interface state machine to start up, and telling us that
238 // hotspot mode is to be started.
239 mTethering.interfaceStatusChanged(mTestIfname, true);
240 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED);
241 mLooper.dispatchAll();
242
Erik Kline8351faa2017-04-17 16:47:23 +0900243 verifyInterfaceServingModeStarted();
244 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
Erik Klineea9cc482017-03-10 19:35:34 +0900245 verify(mNMService, times(1)).setIpForwardingEnabled(true);
246 verify(mNMService, times(1)).startTethering(any(String[].class));
247 verifyNoMoreInteractions(mNMService);
Erik Kline216af6d2017-04-27 20:57:23 +0900248 verify(mWifiManager).updateInterfaceIpState(
249 mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
250 verifyNoMoreInteractions(mWifiManager);
Erik Kline8351faa2017-04-17 16:47:23 +0900251 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
Erik Klineea9cc482017-03-10 19:35:34 +0900252 // UpstreamNetworkMonitor will be started, and will register two callbacks:
253 // a "listen all" and a "track default".
254 verify(mConnectivityManager, times(1)).registerNetworkCallback(
255 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
256 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
257 any(NetworkCallback.class), any(Handler.class));
258 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
259 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
260 verifyNoMoreInteractions(mConnectivityManager);
261
262 // Emulate externally-visible WifiManager effects, when hotspot mode
263 // is being torn down.
264 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
265 mTethering.interfaceRemoved(mTestIfname);
266 mLooper.dispatchAll();
267
268 verify(mNMService, times(1)).untetherInterface(mTestIfname);
269 // TODO: Why is {g,s}etInterfaceConfig() called more than once?
270 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
271 verify(mNMService, atLeastOnce())
272 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
273 verify(mNMService, times(1)).stopTethering();
274 verify(mNMService, times(1)).setIpForwardingEnabled(false);
275 verifyNoMoreInteractions(mNMService);
Erik Kline216af6d2017-04-27 20:57:23 +0900276 verifyNoMoreInteractions(mWifiManager);
Erik Klineea9cc482017-03-10 19:35:34 +0900277 // Asking for the last error after the per-interface state machine
278 // has been reaped yields an unknown interface error.
279 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
280 mTethering.getLastTetherError(mTestIfname));
281 }
282
283 @Test
284 public void workingWifiTethering() throws Exception {
285 when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
Erik Klineceb54c62017-04-18 14:22:25 +0900286 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
Erik Klineea9cc482017-03-10 19:35:34 +0900287
288 // Emulate pressing the WiFi tethering button.
289 mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
290 mLooper.dispatchAll();
Erik Klineceb54c62017-04-18 14:22:25 +0900291 verify(mWifiManager, times(1)).startSoftAp(null);
Erik Klineea9cc482017-03-10 19:35:34 +0900292 verifyNoMoreInteractions(mWifiManager);
293 verifyNoMoreInteractions(mConnectivityManager);
294 verifyNoMoreInteractions(mNMService);
295
296 // Emulate externally-visible WifiManager effects, causing the
297 // per-interface state machine to start up, and telling us that
298 // tethering mode is to be started.
299 mTethering.interfaceStatusChanged(mTestIfname, true);
300 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED);
301 mLooper.dispatchAll();
302
Erik Kline8351faa2017-04-17 16:47:23 +0900303 verifyInterfaceServingModeStarted();
304 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
Erik Klineea9cc482017-03-10 19:35:34 +0900305 verify(mNMService, times(1)).setIpForwardingEnabled(true);
306 verify(mNMService, times(1)).startTethering(any(String[].class));
307 verifyNoMoreInteractions(mNMService);
Erik Kline216af6d2017-04-27 20:57:23 +0900308 verify(mWifiManager).updateInterfaceIpState(
309 mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
310 verifyNoMoreInteractions(mWifiManager);
Erik Kline8351faa2017-04-17 16:47:23 +0900311 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
Erik Klineea9cc482017-03-10 19:35:34 +0900312 // UpstreamNetworkMonitor will be started, and will register two callbacks:
313 // a "listen all" and a "track default".
314 verify(mConnectivityManager, times(1)).registerNetworkCallback(
315 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
316 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
317 any(NetworkCallback.class), any(Handler.class));
318 // In tethering mode, in the default configuration, an explicit request
319 // for a mobile network is also made.
320 verify(mConnectivityManager, atLeastOnce()).getNetworkInfo(anyInt());
321 verify(mConnectivityManager, times(1)).requestNetwork(
322 any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
323 any(Handler.class));
324 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
325 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
326 verifyNoMoreInteractions(mConnectivityManager);
327
328 /////
329 // We do not currently emulate any upstream being found.
330 //
331 // This is why there are no calls to verify mNMService.enableNat() or
332 // mNMService.startInterfaceForwarding().
333 /////
334
335 // Emulate pressing the WiFi tethering button.
336 mTethering.stopTethering(ConnectivityManager.TETHERING_WIFI);
337 mLooper.dispatchAll();
Erik Klineceb54c62017-04-18 14:22:25 +0900338 verify(mWifiManager, times(1)).stopSoftAp();
Erik Klineea9cc482017-03-10 19:35:34 +0900339 verifyNoMoreInteractions(mWifiManager);
340 verifyNoMoreInteractions(mConnectivityManager);
341 verifyNoMoreInteractions(mNMService);
342
343 // Emulate externally-visible WifiManager effects, when tethering mode
344 // is being torn down.
345 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
346 mTethering.interfaceRemoved(mTestIfname);
347 mLooper.dispatchAll();
348
349 verify(mNMService, times(1)).untetherInterface(mTestIfname);
350 // TODO: Why is {g,s}etInterfaceConfig() called more than once?
351 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
352 verify(mNMService, atLeastOnce())
353 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
354 verify(mNMService, times(1)).stopTethering();
355 verify(mNMService, times(1)).setIpForwardingEnabled(false);
356 verifyNoMoreInteractions(mNMService);
Erik Kline216af6d2017-04-27 20:57:23 +0900357 verifyNoMoreInteractions(mWifiManager);
Erik Klineea9cc482017-03-10 19:35:34 +0900358 // Asking for the last error after the per-interface state machine
359 // has been reaped yields an unknown interface error.
360 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
361 mTethering.getLastTetherError(mTestIfname));
362 }
363
Erik Kline1fdc2e22017-05-08 17:56:35 +0900364 @Test
365 public void failureEnablingIpForwarding() throws Exception {
366 when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
367 when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
368 doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(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(WifiManager.WIFI_AP_STATE_ENABLED);
383 mLooper.dispatchAll();
384
385 // Activity caused by test_wlan0 becoming available.
386 verify(mNMService, times(1)).listInterfaces();
387 // We verify get/set called twice here: once for setup and once during
388 // teardown because all events happen over the course of the single
389 // dispatchAll() above.
390 verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
391 verify(mNMService, times(2))
392 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
393 verify(mNMService, times(1)).tetherInterface(mTestIfname);
394 verify(mWifiManager).updateInterfaceIpState(
395 mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
396 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
397 verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
398 // This is called, but will throw.
399 verify(mNMService, times(1)).setIpForwardingEnabled(true);
400 // This never gets called because of the exception thrown above.
401 verify(mNMService, times(0)).startTethering(any(String[].class));
402 // When the master state machine transitions to an error state it tells
403 // downstream interfaces, which causes us to tell Wi-Fi about the error
404 // so it can take down AP mode.
405 verify(mNMService, times(1)).untetherInterface(mTestIfname);
406 verify(mWifiManager).updateInterfaceIpState(
407 mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
408
409 verifyNoMoreInteractions(mWifiManager);
410 verifyNoMoreInteractions(mConnectivityManager);
411 verifyNoMoreInteractions(mNMService);
412 }
413
414 // TODO: Test that a request for hotspot mode doesn't interfere with an
Erik Klineea9cc482017-03-10 19:35:34 +0900415 // already operating tethering mode interface.
Christopher Wiley497c1472016-10-11 13:26:03 -0700416}