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