Merge "Handle Wi-Fi passing explicit interface name and AP/IP mode"

This commit is contained in:
Treehugger Robot
2017-06-02 05:19:31 +00:00
committed by Gerrit Code Review
2 changed files with 126 additions and 42 deletions

View File

@@ -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;
@@ -788,48 +794,80 @@ 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 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);
}
// 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;
if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
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;
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;
if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
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;
}
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);
}
}
// TODO: Consider renaming to something more accurate in its description.
// This method:
// - allows requesting either tethering or local hotspot serving states
@@ -862,22 +900,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;
}
}
@@ -1360,10 +1402,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);
@@ -1381,7 +1423,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);
}
}
}

View File

@@ -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;
@@ -222,12 +228,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 {
verify(mNMService, times(1)).listInterfaces();
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));
@@ -243,18 +259,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));
@@ -295,7 +314,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);
@@ -311,10 +339,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));
@@ -375,6 +407,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);