Merge "p2p: mirgrate p2p into tethering modes" am: 8a9a9adaeb

am: 58d08de0c6

Change-Id: I618c950426976dd1899284d0c249ea7b4e45b479
This commit is contained in:
Jimmy Chen
2019-10-17 23:12:30 -07:00
committed by android-build-merger
8 changed files with 308 additions and 29 deletions

View File

@@ -472,6 +472,14 @@ public class ConnectivityManager {
@SystemApi
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.

View File

@@ -386,6 +386,12 @@
<string-array translatable="false" name="config_tether_wifi_regexs">
</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.*" -->

View File

@@ -1915,6 +1915,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" />

View File

@@ -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)) {
@@ -527,6 +534,7 @@ public class Tethering extends BaseNetworkObserver {
public void untetherAll() {
stopTethering(TETHERING_WIFI);
stopTethering(TETHERING_WIFI_P2P);
stopTethering(TETHERING_USB);
stopTethering(TETHERING_BLUETOOTH);
}
@@ -713,6 +721,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();
@@ -789,6 +799,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
@@ -823,14 +866,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);
@@ -842,7 +882,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;
}
@@ -853,6 +893,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".
@@ -870,7 +924,7 @@ public class Tethering extends BaseNetworkObserver {
}
if (!TextUtils.isEmpty(ifname)) {
maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI);
maybeTrackNewInterfaceLocked(ifname);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(

View File

@@ -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));

View File

@@ -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);

View File

@@ -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;
@@ -255,6 +257,23 @@ public class IpServerTest {
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@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);
@@ -418,6 +437,14 @@ public class IpServerTest {
assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
}
@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 */);

View File

@@ -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.
}