summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/ConnectivityManager.java8
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java74
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java14
-rw-r--r--services/net/java/android/net/ip/IpServer.java5
-rw-r--r--tests/net/java/android/net/ip/IpServerTest.java27
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java202
8 files changed, 308 insertions, 29 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 111a8c48a46c..ace0413e4996 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -473,6 +473,14 @@ public class ConnectivityManager {
public static final int TETHERING_BLUETOOTH = 2;
/**
+ * Wifi P2p tethering type.
+ * Wifi P2p tethering is set through events automatically, and don't
+ * need to start from #startTethering(int, boolean, OnStartTetheringCallback).
+ * @hide
+ */
+ public static final int TETHERING_WIFI_P2P = 3;
+
+ /**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
* @hide
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 72462ad44386..f51c4f3f3c9c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -376,6 +376,12 @@
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
+ Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this
+ should be empty. An example would be "p2p-p2p.*" -->
+ <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
WiMAX interfaces. If the device doesn't want to support tethering over Wifi this
should be empty. An example would be "softap.*" -->
<string-array translatable="false" name="config_tether_wimax_regexs">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9f8baf8e5594..c7e3aa4ab755 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1850,6 +1850,7 @@
<java-symbol type="bool" name="config_tether_upstream_automatic" />
<java-symbol type="array" name="config_tether_usb_regexs" />
<java-symbol type="array" name="config_tether_wifi_regexs" />
+ <java-symbol type="array" name="config_tether_wifi_p2p_regexs" />
<java-symbol type="array" name="config_usbHostBlacklist" />
<java-symbol type="array" name="config_serialPorts" />
<java-symbol type="array" name="radioAttributes" />
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 86d12124785d..0d9f6953d644 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -30,6 +30,7 @@ import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
@@ -77,6 +78,9 @@ import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.util.VersionedBroadcastListener;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -290,6 +294,7 @@ public class Tethering extends BaseNetworkObserver {
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
@@ -354,6 +359,8 @@ public class Tethering extends BaseNetworkObserver {
if (cfg.isWifi(iface)) {
return TETHERING_WIFI;
+ } else if (cfg.isWifiP2p(iface)) {
+ return TETHERING_WIFI_P2P;
} else if (cfg.isUsb(iface)) {
return TETHERING_USB;
} else if (cfg.isBluetooth(iface)) {
@@ -514,6 +521,7 @@ public class Tethering extends BaseNetworkObserver {
public void untetherAll() {
stopTethering(TETHERING_WIFI);
+ stopTethering(TETHERING_WIFI_P2P);
stopTethering(TETHERING_USB);
stopTethering(TETHERING_BLUETOOTH);
}
@@ -700,6 +708,8 @@ public class Tethering extends BaseNetworkObserver {
handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
handleWifiApAction(intent);
+ } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+ handleWifiP2pAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
mLog.log("OBSERVED configuration changed");
updateConfiguration();
@@ -776,6 +786,39 @@ public class Tethering extends BaseNetworkObserver {
}
}
}
+
+ private void handleWifiP2pAction(Intent intent) {
+ if (mConfig.isWifiP2pLegacyTetheringMode()) return;
+
+ final WifiP2pInfo p2pInfo =
+ (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
+ final WifiP2pGroup group =
+ (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
+
+ if (VDBG) {
+ Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
+ }
+
+ if (p2pInfo == null) return;
+ // When a p2p group is disconnected, p2pInfo would be cleared.
+ // group is still valid for detecting whether this device is group owner.
+ if (group == null || !group.isGroupOwner()
+ || TextUtils.isEmpty(group.getInterface())) return;
+
+ synchronized (Tethering.this.mPublicSync) {
+ // Enter below only if this device is Group Owner with a valid interface.
+ if (p2pInfo.groupFormed) {
+ TetherState tetherState = mTetherStates.get(group.getInterface());
+ if (tetherState == null
+ || (tetherState.lastState != IpServer.STATE_TETHERED
+ && tetherState.lastState != IpServer.STATE_LOCAL_ONLY)) {
+ enableWifiIpServingLocked(group.getInterface(), IFACE_IP_MODE_LOCAL_ONLY);
+ }
+ } else {
+ disableWifiP2pIpServingLocked(group.getInterface());
+ }
+ }
+ }
}
@VisibleForTesting
@@ -810,14 +853,11 @@ public class Tethering extends BaseNetworkObserver {
}
}
- private void disableWifiIpServingLocked(String ifname, int apState) {
- mLog.log("Canceling WiFi tethering request - AP_STATE=" + apState);
-
- // 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.
- // TODO: Remove this altogether, once Wi-Fi reliably gives us an
- // interface name with every broadcast.
- mWifiTetherRequested = false;
+ private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) {
+ mLog.log("Canceling WiFi tethering request -"
+ + " type=" + tetheringType
+ + " interface=" + ifname
+ + " state=" + apState);
if (!TextUtils.isEmpty(ifname)) {
final TetherState ts = mTetherStates.get(ifname);
@@ -829,7 +869,7 @@ public class Tethering extends BaseNetworkObserver {
for (int i = 0; i < mTetherStates.size(); i++) {
final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
- if (ipServer.interfaceType() == TETHERING_WIFI) {
+ if (ipServer.interfaceType() == tetheringType) {
ipServer.unwanted();
return;
}
@@ -840,6 +880,20 @@ public class Tethering extends BaseNetworkObserver {
: "specified interface: " + ifname));
}
+ private void disableWifiIpServingLocked(String ifname, int apState) {
+ // 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.
+ // TODO: Remove this altogether, once Wi-Fi reliably gives us an
+ // interface name with every broadcast.
+ mWifiTetherRequested = false;
+
+ disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState);
+ }
+
+ private void disableWifiP2pIpServingLocked(String ifname) {
+ disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* dummy */ 0);
+ }
+
private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
// Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
@@ -857,7 +911,7 @@ public class Tethering extends BaseNetworkObserver {
}
if (!TextUtils.isEmpty(ifname)) {
- maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI);
+ maybeTrackNewInterfaceLocked(ifname);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 1907892c4d87..a1b94ca33944 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -28,6 +28,7 @@ import static com.android.internal.R.array.config_tether_bluetooth_regexs;
import static com.android.internal.R.array.config_tether_dhcp_range;
import static com.android.internal.R.array.config_tether_upstream_types;
import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_wifi_p2p_regexs;
import static com.android.internal.R.array.config_tether_wifi_regexs;
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
@@ -85,6 +86,7 @@ public class TetheringConfiguration {
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
+ public final String[] tetherableWifiP2pRegexs;
public final String[] tetherableBluetoothRegexs;
public final boolean isDunRequired;
public final boolean chooseUpstreamAutomatically;
@@ -110,6 +112,7 @@ public class TetheringConfiguration {
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
+ tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
isDunRequired = checkDunRequired(ctx, subId);
@@ -138,6 +141,15 @@ public class TetheringConfiguration {
return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
}
+ /** Check whether this interface is Wifi P2P interface. */
+ public boolean isWifiP2p(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
+ }
+
+ public boolean isWifiP2pLegacyTetheringMode() {
+ return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
+ }
+
public boolean isBluetooth(String iface) {
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
@@ -152,6 +164,7 @@ public class TetheringConfiguration {
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
+ dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
pw.print("isDunRequired: ");
@@ -178,6 +191,7 @@ public class TetheringConfiguration {
sj.add(String.format("subId:%d", subId));
sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
+ sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
sj.add(String.format("tetherableBluetoothRegexs:%s",
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 6a6a1307723e..3d79bba7bbe3 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -93,6 +93,8 @@ public class IpServer extends StateMachine {
private static final int USB_PREFIX_LENGTH = 24;
private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
+ private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
+ private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
// TODO: have PanService use some visible version of this constant
private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
@@ -403,6 +405,9 @@ public class IpServer extends StateMachine {
} else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
ipAsString = getRandomWifiIPv4Address();
prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
+ } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) {
+ ipAsString = WIFI_P2P_IFACE_ADDR;
+ prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
} else {
// BT configures the interface elsewhere: only start DHCP.
final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR);
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 05912e85426a..b6ccebb3dcd3 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -19,11 +19,13 @@ package android.net.ip;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
@@ -256,6 +258,23 @@ public class IpServerTest {
}
@Test
+ public void canBeTetheredAsWifiP2p() throws Exception {
+ initStateMachine(TETHERING_WIFI_P2P);
+
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+ InOrder inOrder = inOrder(mCallback, mNMService);
+ inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
+ inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
+ inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
+ assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+ }
+
+ @Test
public void handlesFirstUpstreamChange() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
@@ -419,6 +438,14 @@ public class IpServerTest {
}
@Test
+ public void startsDhcpServerOnWifiP2p() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+
+ assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
+ }
+
+ @Test
public void doesNotStartDhcpServerIfDisabled() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index c030c3e9ac86..5f62c08f55f3 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -25,8 +25,10 @@ import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -90,6 +92,9 @@ import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -140,6 +145,7 @@ public class TetheringTest {
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
@@ -216,9 +222,10 @@ public class TetheringTest {
assertTrue("Non-mocked interface " + ifName,
ifName.equals(TEST_USB_IFNAME)
|| ifName.equals(TEST_WLAN_IFNAME)
- || ifName.equals(TEST_MOBILE_IFNAME));
+ || ifName.equals(TEST_MOBILE_IFNAME)
+ || ifName.equals(TEST_P2P_IFNAME));
final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME};
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
@@ -361,6 +368,8 @@ public class TetheringTest {
.thenReturn(new String[] { "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
@@ -369,7 +378,7 @@ public class TetheringTest {
.thenReturn(false);
when(mNMService.listInterfaces())
.thenReturn(new String[] {
- TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
when(mNMService.getInterfaceConfig(anyString()))
.thenReturn(new InterfaceConfiguration());
when(mRouterAdvertisementDaemon.start())
@@ -423,6 +432,31 @@ public class TetheringTest {
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = {
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_WIFI_STATE
+ };
+
+ private void sendWifiP2pConnectionChanged(
+ boolean isGroupFormed, boolean isGroupOwner, String ifname) {
+ WifiP2pInfo p2pInfo = new WifiP2pInfo();
+ p2pInfo.groupFormed = isGroupFormed;
+ p2pInfo.isGroupOwner = isGroupOwner;
+
+ NetworkInfo networkInfo = new NetworkInfo(TYPE_WIFI_P2P, 0, null, null);
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setIsGroupOwner(isGroupOwner);
+ group.setInterface(ifname);
+
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo);
+ intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+ mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
+ P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
+ }
+
private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.putExtra(USB_CONNECTED, connected);
@@ -436,11 +470,11 @@ public class TetheringTest {
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void verifyInterfaceServingModeStarted() throws Exception {
- verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
+ private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
+ verify(mNMService, times(1)).getInterfaceConfig(ifname);
verify(mNMService, times(1))
- .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
+ .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(ifname);
}
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -530,7 +564,7 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
mLooper.dispatchAll();
- verifyInterfaceServingModeStarted();
+ verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
@@ -542,8 +576,9 @@ public class TetheringTest {
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
@@ -552,9 +587,9 @@ public class TetheringTest {
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
- // TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
- verify(mNMService, atLeastOnce())
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
+ verify(mNMService, times(2))
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
@@ -770,7 +805,7 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
- verifyInterfaceServingModeStarted();
+ verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
@@ -785,8 +820,9 @@ public class TetheringTest {
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
/////
// We do not currently emulate any upstream being found.
@@ -809,7 +845,7 @@ public class TetheringTest {
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
- // TODO: Why is {g,s}etInterfaceConfig() called more than once?
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
@@ -857,8 +893,9 @@ public class TetheringTest {
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // There are 3 state change event:
+ // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
+ assertEquals(3, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
@@ -1031,6 +1068,133 @@ public class TetheringTest {
assertEquals(fakeSubId, newConfig.subId);
}
+ private void workingWifiP2pGroupOwner(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
+ verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mNMService, times(1)).setIpForwardingEnabled(true);
+ verify(mNMService, times(1)).startTethering(any(String[].class));
+ verifyNoMoreInteractions(mNMService);
+ verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
+ verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
+
+ assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+
+ // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
+ // is being removed.
+ sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME);
+ mTethering.interfaceRemoved(TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME);
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, times(2))
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).stopTethering();
+ verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream();
+ verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
+ verifyNoMoreInteractions(mNMService);
+ // Asking for the last error after the per-interface state machine
+ // has been reaped yields an unknown interface error.
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+
+ private void workingWifiP2pGroupClient(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).setIpForwardingEnabled(true);
+ verify(mNMService, never()).startTethering(any(String[].class));
+
+ // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
+ // is being removed.
+ sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME);
+ mTethering.interfaceRemoved(TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).stopTethering();
+ verify(mNMService, never()).setIpForwardingEnabled(false);
+ verifyNoMoreInteractions(mNMService);
+ // Asking for the last error after the per-interface state machine
+ // has been reaped yields an unknown interface error.
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwner(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwner(false);
+ }
+
+ private void workingWifiP2pGroupOwnerLegacyMode(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ // change to legacy mode and update tethering information by chaning SIM
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{});
+ final int fakeSubId = 1234;
+ mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
+
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).setIpForwardingEnabled(true);
+ verify(mNMService, never()).startTethering(any(String[].class));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+ @Test
+ public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwnerLegacyMode(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwnerLegacyMode(false);
+ }
+
+ @Test
+ public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupClient(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupClient(false);
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}