Loading services/core/java/com/android/server/connectivity/Tethering.java +74 −32 Original line number Diff line number Diff line Loading @@ -18,7 +18,13 @@ package com.android.server.connectivity; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static com.android.server.ConnectivityService.SHORT_ARG; Loading Loading @@ -800,44 +806,76 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering private void handleWifiApAction(Intent intent) { final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED); synchronized (Tethering.this.mPublicSync) { switch (curState) { case WifiManager.WIFI_AP_STATE_ENABLING: // We can see this state on the way to both enabled and failure states. break; case WifiManager.WIFI_AP_STATE_ENABLED: // When the AP comes up and we've been requested to tether it, do so. // Otherwise, assume it's a local-only hotspot request. final int state = mWifiTetherRequested ? IControlsTethering.STATE_TETHERED : IControlsTethering.STATE_LOCAL_ONLY; tetherMatchingInterfaces(state, ConnectivityManager.TETHERING_WIFI); enableWifiIpServingLocked(ifname, ipmode); break; case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_DISABLING: case WifiManager.WIFI_AP_STATE_FAILED: default: if (DBG) { Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" + curState); disableWifiIpServingLocked(curState); break; } } } } // TODO: Pass in the interface name and, if non-empty, only turn down IP // serving on that one interface. private void disableWifiIpServingLocked(int apState) { if (DBG) Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" + apState); // Tell appropriate interface state machines that they should tear // themselves down. for (int i = 0; i < mTetherStates.size(); i++) { TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine; TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine; if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) { tism.sendMessage( TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); tism.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); break; // There should be at most one of these. } } // Regardless of whether we requested this transition, the AP has gone // down. Don't try to tether again unless we're requested to do so. mWifiTetherRequested = false; break; } private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { // Map wifiIpMode values to IControlsTethering serving states, inferring // from mWifiTetherRequested as a final "best guess". final int ipServingMode; switch (wifiIpMode) { case IFACE_IP_MODE_TETHERED: ipServingMode = IControlsTethering.STATE_TETHERED; break; case IFACE_IP_MODE_LOCAL_ONLY: ipServingMode = IControlsTethering.STATE_LOCAL_ONLY; break; default: // Resort to legacy "guessing" behaviour. // // When the AP comes up and we've been requested to tether it, // do so. Otherwise, assume it's a local-only hotspot request. // // TODO: Once all AP broadcasts are known to include ifname and // mode information delete this code path and log an error. ipServingMode = mWifiTetherRequested ? IControlsTethering.STATE_TETHERED : IControlsTethering.STATE_LOCAL_ONLY; break; } if (!TextUtils.isEmpty(ifname)) { changeInterfaceState(ifname, ipServingMode); } else { tetherMatchingInterfaces(ipServingMode, ConnectivityManager.TETHERING_WIFI); } } Loading Loading @@ -873,22 +911,26 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return; } changeInterfaceState(chosenIface, requestedState); } private void changeInterfaceState(String ifname, int requestedState) { final int result; switch (requestedState) { case IControlsTethering.STATE_UNAVAILABLE: case IControlsTethering.STATE_AVAILABLE: result = untether(chosenIface); result = untether(ifname); break; case IControlsTethering.STATE_TETHERED: case IControlsTethering.STATE_LOCAL_ONLY: result = tether(chosenIface, requestedState); result = tether(ifname, requestedState); break; default: Log.wtf(TAG, "Unknown interface state: " + requestedState); return; } if (result != ConnectivityManager.TETHER_ERROR_NO_ERROR) { Log.e(TAG, "unable start or stop tethering on iface " + chosenIface); Log.e(TAG, "unable start or stop tethering on iface " + ifname); return; } } Loading Loading @@ -1470,10 +1512,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering final String iface = who.interfaceName(); switch (mode) { case IControlsTethering.STATE_TETHERED: mgr.updateInterfaceIpState(iface, WifiManager.IFACE_IP_MODE_TETHERED); mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); break; case IControlsTethering.STATE_LOCAL_ONLY: mgr.updateInterfaceIpState(iface, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); break; default: Log.wtf(TAG, "Unknown active serving mode: " + mode); Loading @@ -1491,7 +1533,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) { if (who.lastError() != ConnectivityManager.TETHER_ERROR_NO_ERROR) { getWifiManager().updateInterfaceIpState( who.interfaceName(), WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR); } } } Loading tests/net/java/com/android/server/connectivity/TetheringTest.java +52 −10 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package com.android.server.connectivity; import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyBoolean; Loading Loading @@ -201,12 +207,22 @@ public class TetheringTest { private void sendWifiApStateChanged(int state) { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state); intent.putExtra(EXTRA_WIFI_AP_STATE, state); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void verifyInterfaceServingModeStarted() throws Exception { private void sendWifiApStateChanged(int state, String ifname, int ipmode) { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(EXTRA_WIFI_AP_STATE, state); intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void verifyInterfaceServingModeStarted(boolean ifnameKnown) throws Exception { if (!ifnameKnown) { verify(mNMService, times(1)).listInterfaces(); } verify(mNMService, times(1)).getInterfaceConfig(mTestIfname); verify(mNMService, times(1)) .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); Loading @@ -222,18 +238,21 @@ public class TetheringTest { mIntents.remove(bcast); } @Test public void workingLocalOnlyHotspot() throws Exception { public void workingLocalOnlyHotspot(boolean enrichedApBroadcast) throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // hotspot mode is to be started. mTethering.interfaceStatusChanged(mTestIfname, true); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED); if (enrichedApBroadcast) { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY); } else { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); } mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); verifyInterfaceServingModeStarted(enrichedApBroadcast); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); Loading Loading @@ -274,7 +293,16 @@ public class TetheringTest { } @Test public void workingWifiTethering() throws Exception { public void workingLocalOnlyHotspotLegacyApBroadcast() throws Exception { workingLocalOnlyHotspot(false); } @Test public void workingLocalOnlyHotspotEnrichedApBroadcast() throws Exception { workingLocalOnlyHotspot(true); } public void workingWifiTethering(boolean enrichedApBroadcast) throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); Loading @@ -290,10 +318,14 @@ public class TetheringTest { // 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); if (enrichedApBroadcast) { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); } else { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); } mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); verifyInterfaceServingModeStarted(enrichedApBroadcast); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); Loading Loading @@ -354,6 +386,16 @@ public class TetheringTest { mTethering.getLastTetherError(mTestIfname)); } @Test public void workingWifiTetheringLegacyApBroadcast() throws Exception { workingWifiTethering(false); } @Test public void workingWifiTetheringEnrichedApBroadcast() throws Exception { workingWifiTethering(true); } @Test public void failureEnablingIpForwarding() throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); Loading Loading
services/core/java/com/android/server/connectivity/Tethering.java +74 −32 Original line number Diff line number Diff line Loading @@ -18,7 +18,13 @@ package com.android.server.connectivity; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static com.android.server.ConnectivityService.SHORT_ARG; Loading Loading @@ -800,44 +806,76 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering private void handleWifiApAction(Intent intent) { final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED); synchronized (Tethering.this.mPublicSync) { switch (curState) { case WifiManager.WIFI_AP_STATE_ENABLING: // We can see this state on the way to both enabled and failure states. break; case WifiManager.WIFI_AP_STATE_ENABLED: // When the AP comes up and we've been requested to tether it, do so. // Otherwise, assume it's a local-only hotspot request. final int state = mWifiTetherRequested ? IControlsTethering.STATE_TETHERED : IControlsTethering.STATE_LOCAL_ONLY; tetherMatchingInterfaces(state, ConnectivityManager.TETHERING_WIFI); enableWifiIpServingLocked(ifname, ipmode); break; case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_DISABLING: case WifiManager.WIFI_AP_STATE_FAILED: default: if (DBG) { Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" + curState); disableWifiIpServingLocked(curState); break; } } } } // TODO: Pass in the interface name and, if non-empty, only turn down IP // serving on that one interface. private void disableWifiIpServingLocked(int apState) { if (DBG) Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" + apState); // Tell appropriate interface state machines that they should tear // themselves down. for (int i = 0; i < mTetherStates.size(); i++) { TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine; TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine; if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) { tism.sendMessage( TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); tism.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); break; // There should be at most one of these. } } // Regardless of whether we requested this transition, the AP has gone // down. Don't try to tether again unless we're requested to do so. mWifiTetherRequested = false; break; } private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { // Map wifiIpMode values to IControlsTethering serving states, inferring // from mWifiTetherRequested as a final "best guess". final int ipServingMode; switch (wifiIpMode) { case IFACE_IP_MODE_TETHERED: ipServingMode = IControlsTethering.STATE_TETHERED; break; case IFACE_IP_MODE_LOCAL_ONLY: ipServingMode = IControlsTethering.STATE_LOCAL_ONLY; break; default: // Resort to legacy "guessing" behaviour. // // When the AP comes up and we've been requested to tether it, // do so. Otherwise, assume it's a local-only hotspot request. // // TODO: Once all AP broadcasts are known to include ifname and // mode information delete this code path and log an error. ipServingMode = mWifiTetherRequested ? IControlsTethering.STATE_TETHERED : IControlsTethering.STATE_LOCAL_ONLY; break; } if (!TextUtils.isEmpty(ifname)) { changeInterfaceState(ifname, ipServingMode); } else { tetherMatchingInterfaces(ipServingMode, ConnectivityManager.TETHERING_WIFI); } } Loading Loading @@ -873,22 +911,26 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return; } changeInterfaceState(chosenIface, requestedState); } private void changeInterfaceState(String ifname, int requestedState) { final int result; switch (requestedState) { case IControlsTethering.STATE_UNAVAILABLE: case IControlsTethering.STATE_AVAILABLE: result = untether(chosenIface); result = untether(ifname); break; case IControlsTethering.STATE_TETHERED: case IControlsTethering.STATE_LOCAL_ONLY: result = tether(chosenIface, requestedState); result = tether(ifname, requestedState); break; default: Log.wtf(TAG, "Unknown interface state: " + requestedState); return; } if (result != ConnectivityManager.TETHER_ERROR_NO_ERROR) { Log.e(TAG, "unable start or stop tethering on iface " + chosenIface); Log.e(TAG, "unable start or stop tethering on iface " + ifname); return; } } Loading Loading @@ -1470,10 +1512,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering final String iface = who.interfaceName(); switch (mode) { case IControlsTethering.STATE_TETHERED: mgr.updateInterfaceIpState(iface, WifiManager.IFACE_IP_MODE_TETHERED); mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); break; case IControlsTethering.STATE_LOCAL_ONLY: mgr.updateInterfaceIpState(iface, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); break; default: Log.wtf(TAG, "Unknown active serving mode: " + mode); Loading @@ -1491,7 +1533,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) { if (who.lastError() != ConnectivityManager.TETHER_ERROR_NO_ERROR) { getWifiManager().updateInterfaceIpState( who.interfaceName(), WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR); } } } Loading
tests/net/java/com/android/server/connectivity/TetheringTest.java +52 −10 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package com.android.server.connectivity; import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyBoolean; Loading Loading @@ -201,12 +207,22 @@ public class TetheringTest { private void sendWifiApStateChanged(int state) { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state); intent.putExtra(EXTRA_WIFI_AP_STATE, state); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void verifyInterfaceServingModeStarted() throws Exception { private void sendWifiApStateChanged(int state, String ifname, int ipmode) { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(EXTRA_WIFI_AP_STATE, state); intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void verifyInterfaceServingModeStarted(boolean ifnameKnown) throws Exception { if (!ifnameKnown) { verify(mNMService, times(1)).listInterfaces(); } verify(mNMService, times(1)).getInterfaceConfig(mTestIfname); verify(mNMService, times(1)) .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); Loading @@ -222,18 +238,21 @@ public class TetheringTest { mIntents.remove(bcast); } @Test public void workingLocalOnlyHotspot() throws Exception { public void workingLocalOnlyHotspot(boolean enrichedApBroadcast) throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // hotspot mode is to be started. mTethering.interfaceStatusChanged(mTestIfname, true); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED); if (enrichedApBroadcast) { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY); } else { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); } mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); verifyInterfaceServingModeStarted(enrichedApBroadcast); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); Loading Loading @@ -274,7 +293,16 @@ public class TetheringTest { } @Test public void workingWifiTethering() throws Exception { public void workingLocalOnlyHotspotLegacyApBroadcast() throws Exception { workingLocalOnlyHotspot(false); } @Test public void workingLocalOnlyHotspotEnrichedApBroadcast() throws Exception { workingLocalOnlyHotspot(true); } public void workingWifiTethering(boolean enrichedApBroadcast) throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); Loading @@ -290,10 +318,14 @@ public class TetheringTest { // 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); if (enrichedApBroadcast) { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); } else { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); } mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); verifyInterfaceServingModeStarted(enrichedApBroadcast); verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); Loading Loading @@ -354,6 +386,16 @@ public class TetheringTest { mTethering.getLastTetherError(mTestIfname)); } @Test public void workingWifiTetheringLegacyApBroadcast() throws Exception { workingWifiTethering(false); } @Test public void workingWifiTetheringEnrichedApBroadcast() throws Exception { workingWifiTethering(true); } @Test public void failureEnablingIpForwarding() throws Exception { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); Loading