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;
}
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
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;
}
}
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);
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);
}
}
}
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;
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));
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));
}
@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);
// 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));
}
@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);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);