Loading services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +27 −52 Original line number Diff line number Diff line Loading @@ -128,14 +128,6 @@ public class TetherInterfaceStateMachine extends StateMachine { private void setLastError(int error) { mLastError = error; if (isErrored()) { if (mUsb) { // note everything's been unwound by this point so nothing to do on // further error.. configureUsbIface(false, mIfaceName); } } } public boolean isAvailable() { Loading Loading @@ -215,9 +207,7 @@ public class TetherInterfaceStateMachine extends StateMachine { public void enter() { if (mUsb) { if (!configureUsbIface(true, mIfaceName)) { mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); transitionTo(mInitialState); return; } Loading @@ -228,17 +218,33 @@ public class TetherInterfaceStateMachine extends StateMachine { } catch (Exception e) { Log.e(TAG, "Error Tethering: " + e.toString()); setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR); transitionTo(mInitialState); return; } if (DBG) Log.d(TAG, "Tethered " + mIfaceName); mTetherController.sendTetherStateChangedBroadcast(); } @Override public void exit() { mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); // Note that at this point, we're leaving the tethered state. We can fail any // of these operations, but it doesn't really change that we have to try them // all in sequence. cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception ee) { Log.e(TAG, "Error untethering after failure!" + ee.toString()); setLastError(ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); Log.e(TAG, "Failed to untether interface: " + ee.toString()); } transitionTo(mInitialState); return; if (mUsb) { configureUsbIface(false, mIfaceName); } if (DBG) Log.d(TAG, "Tethered " + mIfaceName); mTetherController.sendTetherStateChangedBroadcast(); } private void cleanupUpstream() { Loading Loading @@ -275,28 +281,12 @@ public class TetherInterfaceStateMachine extends StateMachine { boolean retValue = true; switch (message.what) { case CMD_TETHER_UNREQUESTED: case CMD_INTERFACE_DOWN: cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception e) { setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); break; } mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); if (message.what == CMD_TETHER_UNREQUESTED) { if (mUsb) { if (!configureUsbIface(false, mIfaceName)) { setLastError( ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); } } transitionTo(mInitialState); } else if (message.what == CMD_INTERFACE_DOWN) { if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName); break; case CMD_INTERFACE_DOWN: transitionTo(mUnavailableState); } if (DBG) Log.d(TAG, "Untethered " + mIfaceName); if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); break; case CMD_TETHER_CONNECTION_CHANGED: String newUpstreamIfaceName = (String)(message.obj); Loading @@ -314,13 +304,6 @@ public class TetherInterfaceStateMachine extends StateMachine { newUpstreamIfaceName); } catch (Exception e) { Log.e(TAG, "Exception enabling Nat: " + e.toString()); try { mNMService.disableNat(mIfaceName, newUpstreamIfaceName); } catch (Exception ee) {} try { mNMService.untetherInterface(mIfaceName); } catch (Exception ee) {} setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR); transitionTo(mInitialState); return true; Loading @@ -333,14 +316,6 @@ public class TetherInterfaceStateMachine extends StateMachine { case CMD_START_TETHERING_ERROR: case CMD_STOP_TETHERING_ERROR: case CMD_SET_DNS_FORWARDERS_ERROR: cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception e) { setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); break; } setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_MASTER_ERROR); break; Loading services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +59 −10 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.connectivity.tethering; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; Loading Loading @@ -54,7 +56,7 @@ public class TetherInterfaceStateMachineTest { private final TestLooper mLooper = new TestLooper(); private TetherInterfaceStateMachine mTestedSm; private void initStateMachine(boolean isUsb) { private void initStateMachine(boolean isUsb) throws Exception { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), isUsb, mNMService, mStatsService, mTetherHelper); mTestedSm.start(); Loading @@ -62,15 +64,17 @@ public class TetherInterfaceStateMachineTest { // the test of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); reset(mNMService, mStatsService, mTetherHelper); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); } private void initTetheredStateMachine(boolean isUsb, String upstreamIface) { private void initTetheredStateMachine(boolean isUsb, String upstreamIface) throws Exception { initStateMachine(isUsb); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } reset(mNMService, mStatsService, mTetherHelper); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); } @Before Loading @@ -92,7 +96,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void shouldDoNothingUntilRequested() { public void shouldDoNothingUntilRequested() throws Exception { initStateMachine(false); final int [] NOOP_COMMANDS = { TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED, Loading @@ -112,7 +116,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void handlesImmediateInterfaceDown() { public void handlesImmediateInterfaceDown() throws Exception { initStateMachine(false); dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); verify(mTetherHelper).sendTetherStateChangedBroadcast(); Loading @@ -124,7 +128,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void canBeTethered() throws RemoteException { public void canBeTethered() throws Exception { initStateMachine(false); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); Loading @@ -144,8 +148,8 @@ public class TetherInterfaceStateMachineTest { dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast(); verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); assertTrue("Should be ready for tethering again", mTestedSm.isAvailable()); Loading @@ -154,12 +158,10 @@ public class TetherInterfaceStateMachineTest { } @Test public void canBeTetheredAsUsb() throws RemoteException { public void canBeTetheredAsUsb() throws Exception { initStateMachine(true); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm); inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); Loading Loading @@ -211,11 +213,11 @@ public class TetherInterfaceStateMachineTest { dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast(); verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); assertTrue("Should be ready for tethering again", mTestedSm.isAvailable()); Loading @@ -223,6 +225,53 @@ public class TetherInterfaceStateMachineTest { assertFalse("Should have no errors", mTestedSm.isErrored()); } @Test public void interfaceDownLeadsToUnavailable() throws Exception { for (boolean shouldThrow : new boolean[]{true, false}) { initTetheredStateMachine(true, null); if (shouldThrow) { doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); } dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); assertFalse("Should not be available", mTestedSm.isAvailable()); assertFalse("Should not be tethered", mTestedSm.isTethered()); } } @Test public void usbShouldBeTornDownOnTetherError() throws Exception { initStateMachine(true); doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); // Initial call is when we transition to the tethered state on request. verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm); // And this call is to notify that we really aren't requested tethering. verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); assertTrue("Expected to see an error reported", mTestedSm.isErrored()); } @Test public void shouldTearDownUsbOnUpstreamError() throws Exception { initTetheredStateMachine(true, null); doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); } /** * Send a command to the state machine under test, and run the event loop to idle. Loading Loading
services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +27 −52 Original line number Diff line number Diff line Loading @@ -128,14 +128,6 @@ public class TetherInterfaceStateMachine extends StateMachine { private void setLastError(int error) { mLastError = error; if (isErrored()) { if (mUsb) { // note everything's been unwound by this point so nothing to do on // further error.. configureUsbIface(false, mIfaceName); } } } public boolean isAvailable() { Loading Loading @@ -215,9 +207,7 @@ public class TetherInterfaceStateMachine extends StateMachine { public void enter() { if (mUsb) { if (!configureUsbIface(true, mIfaceName)) { mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); transitionTo(mInitialState); return; } Loading @@ -228,17 +218,33 @@ public class TetherInterfaceStateMachine extends StateMachine { } catch (Exception e) { Log.e(TAG, "Error Tethering: " + e.toString()); setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR); transitionTo(mInitialState); return; } if (DBG) Log.d(TAG, "Tethered " + mIfaceName); mTetherController.sendTetherStateChangedBroadcast(); } @Override public void exit() { mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); // Note that at this point, we're leaving the tethered state. We can fail any // of these operations, but it doesn't really change that we have to try them // all in sequence. cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception ee) { Log.e(TAG, "Error untethering after failure!" + ee.toString()); setLastError(ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); Log.e(TAG, "Failed to untether interface: " + ee.toString()); } transitionTo(mInitialState); return; if (mUsb) { configureUsbIface(false, mIfaceName); } if (DBG) Log.d(TAG, "Tethered " + mIfaceName); mTetherController.sendTetherStateChangedBroadcast(); } private void cleanupUpstream() { Loading Loading @@ -275,28 +281,12 @@ public class TetherInterfaceStateMachine extends StateMachine { boolean retValue = true; switch (message.what) { case CMD_TETHER_UNREQUESTED: case CMD_INTERFACE_DOWN: cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception e) { setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); break; } mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceStateMachine.this); if (message.what == CMD_TETHER_UNREQUESTED) { if (mUsb) { if (!configureUsbIface(false, mIfaceName)) { setLastError( ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR); } } transitionTo(mInitialState); } else if (message.what == CMD_INTERFACE_DOWN) { if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName); break; case CMD_INTERFACE_DOWN: transitionTo(mUnavailableState); } if (DBG) Log.d(TAG, "Untethered " + mIfaceName); if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); break; case CMD_TETHER_CONNECTION_CHANGED: String newUpstreamIfaceName = (String)(message.obj); Loading @@ -314,13 +304,6 @@ public class TetherInterfaceStateMachine extends StateMachine { newUpstreamIfaceName); } catch (Exception e) { Log.e(TAG, "Exception enabling Nat: " + e.toString()); try { mNMService.disableNat(mIfaceName, newUpstreamIfaceName); } catch (Exception ee) {} try { mNMService.untetherInterface(mIfaceName); } catch (Exception ee) {} setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR); transitionTo(mInitialState); return true; Loading @@ -333,14 +316,6 @@ public class TetherInterfaceStateMachine extends StateMachine { case CMD_START_TETHERING_ERROR: case CMD_STOP_TETHERING_ERROR: case CMD_SET_DNS_FORWARDERS_ERROR: cleanupUpstream(); try { mNMService.untetherInterface(mIfaceName); } catch (Exception e) { setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR); break; } setLastErrorAndTransitionToInitialState( ConnectivityManager.TETHER_ERROR_MASTER_ERROR); break; Loading
services/tests/servicestests/src/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +59 −10 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.connectivity.tethering; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; Loading Loading @@ -54,7 +56,7 @@ public class TetherInterfaceStateMachineTest { private final TestLooper mLooper = new TestLooper(); private TetherInterfaceStateMachine mTestedSm; private void initStateMachine(boolean isUsb) { private void initStateMachine(boolean isUsb) throws Exception { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), isUsb, mNMService, mStatsService, mTetherHelper); mTestedSm.start(); Loading @@ -62,15 +64,17 @@ public class TetherInterfaceStateMachineTest { // the test of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); reset(mNMService, mStatsService, mTetherHelper); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); } private void initTetheredStateMachine(boolean isUsb, String upstreamIface) { private void initTetheredStateMachine(boolean isUsb, String upstreamIface) throws Exception { initStateMachine(isUsb); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } reset(mNMService, mStatsService, mTetherHelper); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); } @Before Loading @@ -92,7 +96,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void shouldDoNothingUntilRequested() { public void shouldDoNothingUntilRequested() throws Exception { initStateMachine(false); final int [] NOOP_COMMANDS = { TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED, Loading @@ -112,7 +116,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void handlesImmediateInterfaceDown() { public void handlesImmediateInterfaceDown() throws Exception { initStateMachine(false); dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); verify(mTetherHelper).sendTetherStateChangedBroadcast(); Loading @@ -124,7 +128,7 @@ public class TetherInterfaceStateMachineTest { } @Test public void canBeTethered() throws RemoteException { public void canBeTethered() throws Exception { initStateMachine(false); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); Loading @@ -144,8 +148,8 @@ public class TetherInterfaceStateMachineTest { dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast(); verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); assertTrue("Should be ready for tethering again", mTestedSm.isAvailable()); Loading @@ -154,12 +158,10 @@ public class TetherInterfaceStateMachineTest { } @Test public void canBeTetheredAsUsb() throws RemoteException { public void canBeTetheredAsUsb() throws Exception { initStateMachine(true); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm); inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); Loading Loading @@ -211,11 +213,11 @@ public class TetherInterfaceStateMachineTest { dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast(); verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); assertTrue("Should be ready for tethering again", mTestedSm.isAvailable()); Loading @@ -223,6 +225,53 @@ public class TetherInterfaceStateMachineTest { assertFalse("Should have no errors", mTestedSm.isErrored()); } @Test public void interfaceDownLeadsToUnavailable() throws Exception { for (boolean shouldThrow : new boolean[]{true, false}) { initTetheredStateMachine(true, null); if (shouldThrow) { doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); } dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); assertFalse("Should not be available", mTestedSm.isAvailable()); assertFalse("Should not be tethered", mTestedSm.isTethered()); } } @Test public void usbShouldBeTornDownOnTetherError() throws Exception { initStateMachine(true); doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); // Initial call is when we transition to the tethered state on request. verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm); // And this call is to notify that we really aren't requested tethering. verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); assertTrue("Expected to see an error reported", mTestedSm.isErrored()); } @Test public void shouldTearDownUsbOnUpstreamError() throws Exception { initTetheredStateMachine(true, null); doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm); } /** * Send a command to the state machine under test, and run the event loop to idle. Loading