Home | History | Annotate | Download | only in connectivity
      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.hardware.usb.UsbManager.USB_CONFIGURED;
     20 import static android.hardware.usb.UsbManager.USB_CONNECTED;
     21 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
     22 import static android.net.ConnectivityManager.TETHERING_WIFI;
     23 import static android.net.ConnectivityManager.TETHERING_USB;
     24 import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
     25 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
     26 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
     27 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
     28 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
     29 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
     30 import static org.junit.Assert.assertEquals;
     31 import static org.junit.Assert.assertTrue;
     32 import static org.mockito.Matchers.anyBoolean;
     33 import static org.mockito.Matchers.anyInt;
     34 import static org.mockito.Matchers.anyString;
     35 import static org.mockito.Matchers.eq;
     36 import static org.mockito.Mockito.any;
     37 import static org.mockito.Mockito.atLeastOnce;
     38 import static org.mockito.Mockito.doThrow;
     39 import static org.mockito.Mockito.times;
     40 import static org.mockito.Mockito.verify;
     41 import static org.mockito.Mockito.verifyNoMoreInteractions;
     42 import static org.mockito.Mockito.when;
     43 
     44 import android.content.BroadcastReceiver;
     45 import android.content.ContentResolver;
     46 import android.content.Context;
     47 import android.content.ContextWrapper;
     48 import android.content.Intent;
     49 import android.content.IntentFilter;
     50 import android.content.pm.ApplicationInfo;
     51 import android.content.res.Resources;
     52 import android.hardware.usb.UsbManager;
     53 import android.net.ConnectivityManager;
     54 import android.net.ConnectivityManager.NetworkCallback;
     55 import android.net.INetworkPolicyManager;
     56 import android.net.INetworkStatsService;
     57 import android.net.InterfaceConfiguration;
     58 import android.net.NetworkRequest;
     59 import android.net.util.SharedLog;
     60 import android.net.wifi.WifiConfiguration;
     61 import android.net.wifi.WifiManager;
     62 import android.os.Handler;
     63 import android.os.INetworkManagementService;
     64 import android.os.PersistableBundle;
     65 import android.os.RemoteException;
     66 import android.os.test.TestLooper;
     67 import android.os.UserHandle;
     68 import android.provider.Settings;
     69 import android.support.test.filters.SmallTest;
     70 import android.support.test.runner.AndroidJUnit4;
     71 import android.telephony.CarrierConfigManager;
     72 import android.test.mock.MockContentResolver;
     73 
     74 import com.android.internal.util.test.BroadcastInterceptingContext;
     75 import com.android.internal.util.test.FakeSettingsProvider;
     76 import com.android.server.connectivity.tethering.OffloadHardwareInterface;
     77 import com.android.server.connectivity.tethering.TetheringDependencies;
     78 
     79 import org.junit.After;
     80 import org.junit.Before;
     81 import org.junit.Test;
     82 import org.junit.runner.RunWith;
     83 import org.mockito.Mock;
     84 import org.mockito.MockitoAnnotations;
     85 
     86 import java.util.ArrayList;
     87 import java.util.Vector;
     88 
     89 @RunWith(AndroidJUnit4.class)
     90 @SmallTest
     91 public 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