Loading services/core/java/com/android/server/connectivity/Tethering.java +71 −52 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.LocalLog; import android.util.Log; import android.util.SparseArray; Loading Loading @@ -146,6 +147,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } private final static int MAX_LOG_RECORDS = 500; private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS); // used to synchronize public access to members private final Object mPublicSync; private final Context mContext; Loading Loading @@ -175,6 +180,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering public Tethering(Context context, INetworkManagementService nmService, INetworkStatsService statsService, INetworkPolicyManager policyManager, Looper looper, MockableSystemProperties systemProperties) { mLocalLog.log("CONSTRUCTED"); mContext = context; mNMService = nmService; mStatsService = statsService; Loading Loading @@ -952,7 +958,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return ConnectivityManager.TETHER_ERROR_NO_ERROR; } // TODO review API - maybe return ArrayList<String> here and below? // TODO review API - figure out how to delete these entirely. public String[] getTetheredIfaces() { ArrayList<String> list = new ArrayList<String>(); synchronized (mPublicSync) { Loading Loading @@ -1072,19 +1078,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering //Add states mInitialState = new InitialState(); addState(mInitialState); mTetherModeAliveState = new TetherModeAliveState(); addState(mTetherModeAliveState); mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); addState(mSetIpForwardingEnabledErrorState); mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); addState(mSetIpForwardingDisabledErrorState); mStartTetheringErrorState = new StartTetheringErrorState(); addState(mStartTetheringErrorState); mStopTetheringErrorState = new StopTetheringErrorState(); addState(mStopTetheringErrorState); mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); addState(mInitialState); addState(mTetherModeAliveState); addState(mSetIpForwardingEnabledErrorState); addState(mSetIpForwardingDisabledErrorState); addState(mStartTetheringErrorState); addState(mStopTetheringErrorState); addState(mSetDnsForwardersErrorState); mNotifyList = new ArrayList<>(); Loading @@ -1092,6 +1098,29 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering setInitialState(mInitialState); } class InitialState extends State { @Override public boolean processMessage(Message message) { maybeLogMessage(this, message.what); switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; default: return NOT_HANDLED; } return HANDLED; } } class TetherMasterUtilState extends State { @Override public boolean processMessage(Message m) { Loading @@ -1112,6 +1141,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.setIpForwardingEnabled(true); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mSetIpForwardingEnabledErrorState); return false; } Loading @@ -1124,10 +1154,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering mNMService.stopTethering(); mNMService.startTethering(cfg.dhcpRanges); } catch (Exception ee) { mLocalLog.log("ERROR " + ee); transitionTo(mStartTetheringErrorState); return false; } } mLocalLog.log("SET master tether settings: ON"); return true; } Loading @@ -1135,16 +1167,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.stopTethering(); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mStopTetheringErrorState); return false; } try { mNMService.setIpForwardingEnabled(false); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mSetIpForwardingDisabledErrorState); return false; } transitionTo(mInitialState); mLocalLog.log("SET master tether settings: OFF"); return true; } Loading Loading @@ -1268,16 +1303,15 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering // TODO: remove this invocation of NetworkUtils.makeStrings(). dnsServers = NetworkUtils.makeStrings(dnses); } if (VDBG) { Log.d(TAG, "Setting DNS forwarders: Network=" + network + ", dnsServers=" + Arrays.toString(dnsServers)); } try { mNMService.setDnsForwarders(network, dnsServers); mLocalLog.log(String.format( "SET DNS forwarders: network=%s dnsServers=[%s]", network, Arrays.toString(dnsServers))); } catch (Exception e) { // TODO: Investigate how this can fail and what exactly // happens if/when such failures occur. Log.e(TAG, "Setting DNS forwarders failed!"); mLocalLog.log("ERROR setting DNS forwarders failed, " + e); transitionTo(mSetDnsForwardersErrorState); } } Loading Loading @@ -1463,31 +1497,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } class InitialState extends State { @Override public boolean processMessage(Message message) { maybeLogMessage(this, message.what); boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; default: retValue = false; break; } return retValue; } } class TetherModeAliveState extends TetherMasterUtilState { final SimChangeListener simChange = new SimChangeListener(mContext); boolean mUpstreamWanted = false; Loading @@ -1495,8 +1504,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering @Override public void enter() { // TODO: examine if we should check the return value. turnOnMasterTetherSettings(); // may transition us out // If turning on master tether settings fails, we have already // transitioned to an error state; exit early. if (!turnOnMasterTetherSettings()) { return; } simChange.startListening(); mUpstreamNetworkMonitor.start(); mOffloadController.start(); Loading Loading @@ -1549,8 +1562,11 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering handleInterfaceServingStateInactive(who); if (mNotifyList.isEmpty()) { turnOffMasterTetherSettings(); // transitions appropriately } else { // transitions appropriately turnOffMasterTetherSettings(); break; } if (DBG) { Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + " live requests:"); Loading @@ -1558,7 +1574,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering Log.d(TAG, " " + o); } } } // If there has been a change and an upstream is no // longer desired, release any mobile requests. final boolean previousUpstreamWanted = updateUpstreamWanted(); Loading Loading @@ -1770,6 +1785,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering pw.println("Upstream wanted: " + upstreamWanted()); pw.decreaseIndent(); } pw.println("Log:"); pw.increaseIndent(); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); pw.decreaseIndent(); } Loading @@ -1786,10 +1807,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } if (DBG) { Log.d(TAG, "iface " + iface + " notified that it was in state " + state + " with error " + error); } mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); try { // Notify that we're tethering (or not) this interface. Loading tests/net/java/com/android/server/connectivity/TetheringTest.java +54 −2 Original line number Diff line number Diff line Loading @@ -18,12 +18,13 @@ package com.android.server.connectivity; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; Loading @@ -47,6 +48,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; import android.os.UserHandle; import android.support.test.filters.SmallTest; Loading Loading @@ -352,6 +354,56 @@ public class TetheringTest { mTethering.getLastTetherError(mTestIfname)); } // TODO: Test that a request for hotspot mode doesn't interface with an @Test public void failureEnablingIpForwarding() throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); // Emulate pressing the WiFi tethering button. mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startSoftAp(null); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // tethering mode is to be started. mTethering.interfaceStatusChanged(mTestIfname, true); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); // Activity caused by test_wlan0 becoming available. verify(mNMService, times(1)).listInterfaces(); // We verify get/set called twice here: once for setup and once during // teardown because all events happen over the course of the single // dispatchAll() above. verify(mNMService, times(2)).getInterfaceConfig(mTestIfname); verify(mNMService, times(2)) .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); verify(mNMService, times(1)).tetherInterface(mTestIfname); verify(mWifiManager).updateInterfaceIpState( mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); // This is called, but will throw. verify(mNMService, times(1)).setIpForwardingEnabled(true); // This never gets called because of the exception thrown above. verify(mNMService, times(0)).startTethering(any(String[].class)); // When the master state machine transitions to an error state it tells // downstream interfaces, which causes us to tell Wi-Fi about the error // so it can take down AP mode. verify(mNMService, times(1)).untetherInterface(mTestIfname); verify(mWifiManager).updateInterfaceIpState( mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); } // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } Loading
services/core/java/com/android/server/connectivity/Tethering.java +71 −52 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.LocalLog; import android.util.Log; import android.util.SparseArray; Loading Loading @@ -146,6 +147,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } private final static int MAX_LOG_RECORDS = 500; private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS); // used to synchronize public access to members private final Object mPublicSync; private final Context mContext; Loading Loading @@ -175,6 +180,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering public Tethering(Context context, INetworkManagementService nmService, INetworkStatsService statsService, INetworkPolicyManager policyManager, Looper looper, MockableSystemProperties systemProperties) { mLocalLog.log("CONSTRUCTED"); mContext = context; mNMService = nmService; mStatsService = statsService; Loading Loading @@ -952,7 +958,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return ConnectivityManager.TETHER_ERROR_NO_ERROR; } // TODO review API - maybe return ArrayList<String> here and below? // TODO review API - figure out how to delete these entirely. public String[] getTetheredIfaces() { ArrayList<String> list = new ArrayList<String>(); synchronized (mPublicSync) { Loading Loading @@ -1072,19 +1078,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering //Add states mInitialState = new InitialState(); addState(mInitialState); mTetherModeAliveState = new TetherModeAliveState(); addState(mTetherModeAliveState); mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); addState(mSetIpForwardingEnabledErrorState); mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); addState(mSetIpForwardingDisabledErrorState); mStartTetheringErrorState = new StartTetheringErrorState(); addState(mStartTetheringErrorState); mStopTetheringErrorState = new StopTetheringErrorState(); addState(mStopTetheringErrorState); mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); addState(mInitialState); addState(mTetherModeAliveState); addState(mSetIpForwardingEnabledErrorState); addState(mSetIpForwardingDisabledErrorState); addState(mStartTetheringErrorState); addState(mStopTetheringErrorState); addState(mSetDnsForwardersErrorState); mNotifyList = new ArrayList<>(); Loading @@ -1092,6 +1098,29 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering setInitialState(mInitialState); } class InitialState extends State { @Override public boolean processMessage(Message message) { maybeLogMessage(this, message.what); switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; default: return NOT_HANDLED; } return HANDLED; } } class TetherMasterUtilState extends State { @Override public boolean processMessage(Message m) { Loading @@ -1112,6 +1141,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.setIpForwardingEnabled(true); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mSetIpForwardingEnabledErrorState); return false; } Loading @@ -1124,10 +1154,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering mNMService.stopTethering(); mNMService.startTethering(cfg.dhcpRanges); } catch (Exception ee) { mLocalLog.log("ERROR " + ee); transitionTo(mStartTetheringErrorState); return false; } } mLocalLog.log("SET master tether settings: ON"); return true; } Loading @@ -1135,16 +1167,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.stopTethering(); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mStopTetheringErrorState); return false; } try { mNMService.setIpForwardingEnabled(false); } catch (Exception e) { mLocalLog.log("ERROR " + e); transitionTo(mSetIpForwardingDisabledErrorState); return false; } transitionTo(mInitialState); mLocalLog.log("SET master tether settings: OFF"); return true; } Loading Loading @@ -1268,16 +1303,15 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering // TODO: remove this invocation of NetworkUtils.makeStrings(). dnsServers = NetworkUtils.makeStrings(dnses); } if (VDBG) { Log.d(TAG, "Setting DNS forwarders: Network=" + network + ", dnsServers=" + Arrays.toString(dnsServers)); } try { mNMService.setDnsForwarders(network, dnsServers); mLocalLog.log(String.format( "SET DNS forwarders: network=%s dnsServers=[%s]", network, Arrays.toString(dnsServers))); } catch (Exception e) { // TODO: Investigate how this can fail and what exactly // happens if/when such failures occur. Log.e(TAG, "Setting DNS forwarders failed!"); mLocalLog.log("ERROR setting DNS forwarders failed, " + e); transitionTo(mSetDnsForwardersErrorState); } } Loading Loading @@ -1463,31 +1497,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } class InitialState extends State { @Override public boolean processMessage(Message message) { maybeLogMessage(this, message.what); boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; default: retValue = false; break; } return retValue; } } class TetherModeAliveState extends TetherMasterUtilState { final SimChangeListener simChange = new SimChangeListener(mContext); boolean mUpstreamWanted = false; Loading @@ -1495,8 +1504,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering @Override public void enter() { // TODO: examine if we should check the return value. turnOnMasterTetherSettings(); // may transition us out // If turning on master tether settings fails, we have already // transitioned to an error state; exit early. if (!turnOnMasterTetherSettings()) { return; } simChange.startListening(); mUpstreamNetworkMonitor.start(); mOffloadController.start(); Loading Loading @@ -1549,8 +1562,11 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering handleInterfaceServingStateInactive(who); if (mNotifyList.isEmpty()) { turnOffMasterTetherSettings(); // transitions appropriately } else { // transitions appropriately turnOffMasterTetherSettings(); break; } if (DBG) { Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + " live requests:"); Loading @@ -1558,7 +1574,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering Log.d(TAG, " " + o); } } } // If there has been a change and an upstream is no // longer desired, release any mobile requests. final boolean previousUpstreamWanted = updateUpstreamWanted(); Loading Loading @@ -1770,6 +1785,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering pw.println("Upstream wanted: " + upstreamWanted()); pw.decreaseIndent(); } pw.println("Log:"); pw.increaseIndent(); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); pw.decreaseIndent(); } Loading @@ -1786,10 +1807,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } if (DBG) { Log.d(TAG, "iface " + iface + " notified that it was in state " + state + " with error " + error); } mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); try { // Notify that we're tethering (or not) this interface. Loading
tests/net/java/com/android/server/connectivity/TetheringTest.java +54 −2 Original line number Diff line number Diff line Loading @@ -18,12 +18,13 @@ package com.android.server.connectivity; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; Loading @@ -47,6 +48,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; import android.os.UserHandle; import android.support.test.filters.SmallTest; Loading Loading @@ -352,6 +354,56 @@ public class TetheringTest { mTethering.getLastTetherError(mTestIfname)); } // TODO: Test that a request for hotspot mode doesn't interface with an @Test public void failureEnablingIpForwarding() throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); // Emulate pressing the WiFi tethering button. mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startSoftAp(null); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // tethering mode is to be started. mTethering.interfaceStatusChanged(mTestIfname, true); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); // Activity caused by test_wlan0 becoming available. verify(mNMService, times(1)).listInterfaces(); // We verify get/set called twice here: once for setup and once during // teardown because all events happen over the course of the single // dispatchAll() above. verify(mNMService, times(2)).getInterfaceConfig(mTestIfname); verify(mNMService, times(2)) .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); verify(mNMService, times(1)).tetherInterface(mTestIfname); verify(mWifiManager).updateInterfaceIpState( mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); // This is called, but will throw. verify(mNMService, times(1)).setIpForwardingEnabled(true); // This never gets called because of the exception thrown above. verify(mNMService, times(0)).startTethering(any(String[].class)); // When the master state machine transitions to an error state it tells // downstream interfaces, which causes us to tell Wi-Fi about the error // so it can take down AP mode. verify(mNMService, times(1)).untetherInterface(mTestIfname); verify(mWifiManager).updateInterfaceIpState( mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); } // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. }