Merge "Handle Wi-Fi passing explicit interface name and AP/IP mode" am: 3478ff8c9e
am: 1536453059
Change-Id: I4a9a99c72f8a1d70355d1bf910530ca50cab8a77
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user