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