Add unit test of tethering local network agent feature
Test: atest TetheringTests:android.net.ip.IpServerTest \
ConnectivityCoverageTests:android.net.ip.IpServerTest \
ConnectivityCoverageTests:com.android.networkstack.tethering \
--update-device
(Also on the next release config)
Bug: 349487600
Change-Id: I56e0ec106f9398225b162fa94e3ccdb94f413049
This commit is contained in:
parent
d55bd89bd6
commit
396dd9b1e0
2 changed files with 303 additions and 35 deletions
|
|
@ -28,6 +28,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
|
|||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||
import static android.net.ip.IpServer.STATE_AVAILABLE;
|
||||
|
|
@ -37,7 +38,9 @@ import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
|||
import static android.net.ip.IpServer.getTetherableIpv6Prefixes;
|
||||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
|
||||
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHERING_LOCAL_NETWORK_AGENT;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -53,6 +56,7 @@ import static org.mockito.Matchers.eq;
|
|||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
|
@ -64,7 +68,7 @@ import static org.mockito.Mockito.verify;
|
|||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.usage.NetworkStatsManager;
|
||||
import android.content.Context;
|
||||
import android.net.INetd;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.InterfaceConfigurationParcel;
|
||||
|
|
@ -72,6 +76,8 @@ import android.net.IpPrefix;
|
|||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.MacAddress;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkAgent;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.TetheringManager.TetheringRequest;
|
||||
import android.net.dhcp.DhcpServerCallbacks;
|
||||
|
|
@ -83,8 +89,10 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
|||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.test.TestLooper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
|
@ -100,6 +108,7 @@ import com.android.networkstack.tethering.util.PrefixUtils;
|
|||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
|
|
@ -122,6 +131,16 @@ public class IpServerTest {
|
|||
@Rule
|
||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||
|
||||
final ArrayMap<String, Boolean> mFeatureFlags = new ArrayMap<>();
|
||||
// This will set feature flags from @FeatureFlag annotations
|
||||
// into the map before setUp() runs.
|
||||
@Rule
|
||||
public final SetFeatureFlagsRule mSetFeatureFlagsRule =
|
||||
new SetFeatureFlagsRule((name, enabled) -> {
|
||||
mFeatureFlags.put(name, enabled);
|
||||
return null;
|
||||
}, (name) -> mFeatureFlags.getOrDefault(name, false));
|
||||
|
||||
private static final String IFACE_NAME = "testnet1";
|
||||
private static final String UPSTREAM_IFACE = "upstream0";
|
||||
private static final String UPSTREAM_IFACE2 = "upstream1";
|
||||
|
|
@ -164,6 +183,7 @@ public class IpServerTest {
|
|||
new LinkAddress("2001:db8:0:abcd::168/64"));
|
||||
private static final Set<IpPrefix> UPSTREAM_PREFIXES2 = Set.of(
|
||||
new IpPrefix("2001:db8:0:1234::/64"), new IpPrefix("2001:db8:0:abcd::/64"));
|
||||
private static final int TEST_NET_ID = 123;
|
||||
|
||||
@Mock private INetd mNetd;
|
||||
@Mock private IpServer.Callback mCallback;
|
||||
|
|
@ -173,10 +193,11 @@ public class IpServerTest {
|
|||
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
||||
@Mock private IpServer.Dependencies mDependencies;
|
||||
@Mock private RoutingCoordinatorManager mRoutingCoordinatorManager;
|
||||
@Mock private NetworkStatsManager mStatsManager;
|
||||
@Mock private TetheringConfiguration mTetherConfig;
|
||||
@Mock private TetheringMetrics mTetheringMetrics;
|
||||
@Mock private BpfCoordinator mBpfCoordinator;
|
||||
@Mock private Context mContext;
|
||||
@Mock private NetworkAgent mNetworkAgent;
|
||||
|
||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||
|
||||
|
|
@ -205,6 +226,18 @@ public class IpServerTest {
|
|||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
|
||||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
|
||||
when(mDependencies.getInterfaceParams(IPSEC_IFACE)).thenReturn(IPSEC_IFACE_PARAMS);
|
||||
doAnswer(
|
||||
invocation -> mFeatureFlags.getOrDefault((String) invocation.getArgument(1), false)
|
||||
).when(mDependencies).isFeatureEnabled(any(), anyString());
|
||||
if (isAtLeastV()) {
|
||||
when(mDependencies.makeNetworkAgent(any(), any(), anyString(), anyInt(), any()))
|
||||
.thenReturn(mNetworkAgent);
|
||||
// Mock the returned network and modifying the status.
|
||||
final Network network = mock(Network.class);
|
||||
doReturn(TEST_NET_ID).when(network).getNetId();
|
||||
doReturn(network).when(mNetworkAgent).register();
|
||||
doReturn(network).when(mNetworkAgent).getNetwork();
|
||||
}
|
||||
|
||||
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
||||
mInterfaceConfiguration.flags = new String[0];
|
||||
|
|
@ -294,10 +327,9 @@ public class IpServerTest {
|
|||
private IpServer createIpServer(final int interfaceType) {
|
||||
mLooper = new TestLooper();
|
||||
mHandler = new Handler(mLooper.getLooper());
|
||||
return new IpServer(IFACE_NAME, mHandler, interfaceType, mSharedLog, mNetd, mBpfCoordinator,
|
||||
mRoutingCoordinatorManager, mCallback, mTetherConfig,
|
||||
return new IpServer(IFACE_NAME, mContext, mHandler, interfaceType, mSharedLog, mNetd,
|
||||
mBpfCoordinator, mRoutingCoordinatorManager, mCallback, mTetherConfig,
|
||||
mTetheringMetrics, mDependencies);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -342,6 +374,10 @@ public class IpServerTest {
|
|||
verifyNoMoreInteractions(mNetd, mCallback);
|
||||
}
|
||||
|
||||
private boolean isTetheringNetworkAgentFeatureEnabled() {
|
||||
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canBeTetheredAsBluetooth() throws Exception {
|
||||
initStateMachine(TETHERING_BLUETOOTH);
|
||||
|
|
@ -360,10 +396,16 @@ public class IpServerTest {
|
|||
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||
}
|
||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
// One for ipv4 route, one for ipv6 link local route.
|
||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
||||
any(), any());
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
inOrder.verify(mNetd, never())
|
||||
.networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
// One for ipv4 route, one for ipv6 link local route.
|
||||
inOrder.verify(mNetd, times(2))
|
||||
.networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), any(), any());
|
||||
}
|
||||
inOrder.verify(mCallback).updateInterfaceState(
|
||||
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||
inOrder.verify(mCallback).updateLinkProperties(
|
||||
|
|
@ -379,7 +421,11 @@ public class IpServerTest {
|
|||
InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
|
||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
}
|
||||
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
||||
// happen after T. Before T, the interface configuration control in bluetooth side.
|
||||
if (isAtLeastT()) {
|
||||
|
|
@ -411,9 +457,15 @@ public class IpServerTest {
|
|||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
||||
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
||||
any(), any());
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
inOrder.verify(mNetd, never())
|
||||
.networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
inOrder.verify(mNetd, times(2))
|
||||
.networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), any(), any());
|
||||
}
|
||||
inOrder.verify(mCallback).updateInterfaceState(
|
||||
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||
inOrder.verify(mCallback).updateLinkProperties(
|
||||
|
|
@ -592,7 +644,11 @@ public class IpServerTest {
|
|||
inOrder.verify(mBpfCoordinator).clearAllIpv6Rules(mIpServer);
|
||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||
}
|
||||
inOrder.verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
||||
inOrder.verify(mRoutingCoordinatorManager).releaseDownstream(any());
|
||||
|
|
@ -1058,6 +1114,162 @@ public class IpServerTest {
|
|||
return true;
|
||||
}
|
||||
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_tetheringAgentEnabled() throws Exception {
|
||||
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_GLOBAL, true);
|
||||
}
|
||||
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT, enabled = false)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_tetheringAgentDisabled() throws Exception {
|
||||
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_GLOBAL, false);
|
||||
}
|
||||
|
||||
// Verify Tethering Network Agent feature doesn't affect Wi-fi P2P Group Owner although
|
||||
// the code is mostly shared.
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_p2pGroupOwnerAgentDisabled() throws Exception {
|
||||
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, false);
|
||||
}
|
||||
|
||||
private void doTestTetheringNetworkAgent(int scope, boolean expectAgentEnabled)
|
||||
throws Exception {
|
||||
initStateMachine(TETHERING_USB);
|
||||
|
||||
final InOrder inOrder = inOrder(mNetworkAgent, mNetd);
|
||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||
0, createMockTetheringRequest(scope));
|
||||
|
||||
inOrder.verify(mNetworkAgent, expectAgentEnabled ? times(1) : never()).register();
|
||||
inOrder.verify(mNetd, times(1)).tetherInterfaceAdd(anyString());
|
||||
if (expectAgentEnabled) {
|
||||
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||
inOrder.verify(mNetworkAgent, times(1)).sendLinkProperties(any());
|
||||
inOrder.verify(mNetworkAgent, times(1)).markConnected();
|
||||
} else {
|
||||
inOrder.verify(mNetd, times(1)).networkAddInterface(anyInt(), anyString());
|
||||
inOrder.verify(mNetd, times(2)).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||
}
|
||||
|
||||
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||
if (expectAgentEnabled) {
|
||||
inOrder.verify(mNetworkAgent, times(1)).unregister();
|
||||
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
inOrder.verify(mNetworkAgent, never()).unregister();
|
||||
inOrder.verify(mNetd, times(1)).networkRemoveInterface(anyInt(), anyString());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if the registration failed, tethering can be gracefully shutdown.
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_registerThrows() throws Exception {
|
||||
initStateMachine(TETHERING_USB);
|
||||
|
||||
final InOrder inOrder = inOrder(mNetworkAgent, mNetd, mCallback);
|
||||
doReturn(null).when(mNetworkAgent).getNetwork();
|
||||
doThrow(IllegalStateException.class).when(mNetworkAgent).register();
|
||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||
|
||||
inOrder.verify(mNetworkAgent).register();
|
||||
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||
inOrder.verify(mNetworkAgent, never()).unregister();
|
||||
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||
inOrder.verify(mCallback).updateInterfaceState(
|
||||
mIpServer, STATE_AVAILABLE, TETHER_ERROR_SERVICE_UNAVAIL);
|
||||
}
|
||||
|
||||
// Verify if the network creation failed, tethering can be gracefully shutdown.
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_netdThrows() throws Exception {
|
||||
initStateMachine(TETHERING_USB);
|
||||
|
||||
final InOrder inOrder = inOrder(mNetworkAgent, mNetd, mCallback);
|
||||
doThrow(ServiceSpecificException.class).when(mNetd).tetherInterfaceAdd(any());
|
||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||
|
||||
inOrder.verify(mNetworkAgent).register();
|
||||
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||
inOrder.verify(mNetworkAgent).unregister();
|
||||
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||
inOrder.verify(mCallback).updateInterfaceState(
|
||||
mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
|
||||
}
|
||||
|
||||
// Verify when IPv6 address update, set routes accordingly.
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||
@Test
|
||||
public void testTetheringNetworkAgent_ipv6AddressUpdate() throws Exception {
|
||||
initStateMachine(TETHERING_USB);
|
||||
|
||||
final InOrder inOrder = inOrder(mNetworkAgent, mNetd);
|
||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||
|
||||
inOrder.verify(mNetworkAgent).register();
|
||||
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||
|
||||
// Ipv6 link local route won't show up in the LinkProperties, so just
|
||||
// verify ipv4 route.
|
||||
final ArgumentCaptor<LinkProperties> lpCaptor =
|
||||
ArgumentCaptor.forClass(LinkProperties.class);
|
||||
inOrder.verify(mNetworkAgent).sendLinkProperties(lpCaptor.capture());
|
||||
final RouteInfo expectedIpv4Route = new RouteInfo(PrefixUtils.asIpPrefix(mTestAddress),
|
||||
null, IFACE_NAME, RouteInfo.RTN_UNICAST);
|
||||
assertRoutes(List.of(expectedIpv4Route), lpCaptor.getValue().getRoutes());
|
||||
assertEquals(IFACE_NAME, lpCaptor.getValue().getInterfaceName());
|
||||
|
||||
inOrder.verify(mNetworkAgent).markConnected();
|
||||
|
||||
// Mock ipv4-only upstream show up.
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
// Verify LinkProperties is updated when IPv6 connectivity is available.
|
||||
final LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||
lp.setLinkAddresses(UPSTREAM_ADDRESSES);
|
||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
|
||||
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||
inOrder.verify(mNetworkAgent).sendLinkProperties(lpCaptor.capture());
|
||||
|
||||
// Expect one Ipv4 route, plus one Ipv6 route.
|
||||
final RouteInfo expectedIpv6Route = new RouteInfo(UPSTREAM_PREFIXES.toArray(
|
||||
new IpPrefix[0])[0], null, IFACE_NAME, RouteInfo.RTN_UNICAST);
|
||||
assertRoutes(List.of(expectedIpv4Route, expectedIpv6Route),
|
||||
lpCaptor.getValue().getRoutes());
|
||||
assertEquals(IFACE_NAME, lpCaptor.getValue().getInterfaceName());
|
||||
|
||||
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||
inOrder.verify(mNetworkAgent).unregister();
|
||||
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||
}
|
||||
|
||||
private void assertRoutes(List<RouteInfo> expectedRoutes, List<RouteInfo> actualRoutes) {
|
||||
assertTrue("Expected Routes: " + expectedRoutes + ", but got: " + actualRoutes,
|
||||
expectedRoutes.equals(actualRoutes));
|
||||
}
|
||||
|
||||
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
public void dadProxyUpdates() throws Exception {
|
||||
InOrder inOrder = inOrder(mDadProxy);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
|
||||
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
|
||||
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
||||
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
|
||||
|
|
@ -83,6 +84,7 @@ import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOA
|
|||
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
|
||||
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
|
||||
import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHERING_LOCAL_NETWORK_AGENT;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
|
||||
|
|
@ -192,6 +194,7 @@ import android.telephony.CarrierConfigManager;
|
|||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -219,6 +222,7 @@ import com.android.testutils.DevSdkIgnoreRule;
|
|||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
import com.android.testutils.MiscAsserts;
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
|
@ -250,6 +254,16 @@ import java.util.concurrent.Executor;
|
|||
public class TetheringTest {
|
||||
@Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||
|
||||
final ArrayMap<String, Boolean> mFeatureFlags = new ArrayMap<>();
|
||||
// This will set feature flags from @FeatureFlag annotations
|
||||
// into the map before setUp() runs.
|
||||
@Rule
|
||||
public final SetFeatureFlagsRule mSetFeatureFlagsRule =
|
||||
new SetFeatureFlagsRule((name, enabled) -> {
|
||||
mFeatureFlags.put(name, enabled);
|
||||
return null;
|
||||
}, (name) -> mFeatureFlags.getOrDefault(name, false));
|
||||
|
||||
private static final int IFINDEX_OFFSET = 100;
|
||||
|
||||
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
||||
|
|
@ -461,6 +475,11 @@ public class TetheringTest {
|
|||
public void setOnDhcpServerCreatedResult(final int result) {
|
||||
mOnDhcpServerCreatedResult = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeatureEnabled(Context context, String name) {
|
||||
return mFeatureFlags.getOrDefault(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
public class MockTetheringDependencies extends TetheringDependencies {
|
||||
|
|
@ -954,12 +973,18 @@ public class TetheringTest {
|
|||
verifyNoMoreInteractions(mCm);
|
||||
}
|
||||
|
||||
private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
|
||||
private void verifyInterfaceServingModeStarted(String ifname, boolean expectAgentEnabled)
|
||||
throws Exception {
|
||||
verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
||||
verify(mNetd).tetherInterfaceAdd(ifname);
|
||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
|
||||
anyString(), anyString());
|
||||
if (expectAgentEnabled) {
|
||||
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
|
||||
anyString(), anyString());
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
|
||||
|
|
@ -1061,10 +1086,18 @@ public class TetheringTest {
|
|||
failingLocalOnlyHotspotLegacyApBroadcast(false);
|
||||
}
|
||||
|
||||
private void verifyStopHotpot() throws Exception {
|
||||
private boolean isTetheringNetworkAgentFeatureEnabled() {
|
||||
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
|
||||
}
|
||||
|
||||
private void verifyStopHotpot(boolean isLocalOnly) throws Exception {
|
||||
verify(mNetd).tetherApplyDnsInterfaces();
|
||||
verify(mNetd).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
if (!isLocalOnly && isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
}
|
||||
// interfaceSetCfg() called once for enabling and twice disabling IPv4.
|
||||
verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
||||
verify(mNetd).tetherStop();
|
||||
|
|
@ -1083,7 +1116,8 @@ public class TetheringTest {
|
|||
}
|
||||
|
||||
private void verifyStartHotspot(boolean isLocalOnly) throws Exception {
|
||||
verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
|
||||
final boolean expectAgentEnabled = !isLocalOnly && isTetheringNetworkAgentFeatureEnabled();
|
||||
verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME, expectAgentEnabled);
|
||||
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||
verify(mWifiManager).updateInterfaceIpState(
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
||||
|
|
@ -1127,7 +1161,7 @@ public class TetheringTest {
|
|||
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
||||
mLooper.dispatchAll();
|
||||
|
||||
verifyStopHotpot();
|
||||
verifyStopHotpot(true /* isLocalOnly */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2073,7 +2107,7 @@ public class TetheringTest {
|
|||
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
||||
mLooper.dispatchAll();
|
||||
|
||||
verifyStopHotpot();
|
||||
verifyStopHotpot(false /* isLocalOnly */);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -2218,9 +2252,14 @@ public class TetheringTest {
|
|||
// code is refactored the two calls during shutdown will revert to one.
|
||||
verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
|
||||
verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME);
|
||||
verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
|
||||
anyString(), anyString());
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
|
||||
anyString(), anyString());
|
||||
}
|
||||
verify(mWifiManager).updateInterfaceIpState(
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
||||
verify(mWifiManager).updateInterfaceIpState(
|
||||
|
|
@ -2239,7 +2278,11 @@ public class TetheringTest {
|
|||
// so it can take down AP mode.
|
||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||
verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
||||
verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||
}
|
||||
verify(mWifiManager).updateInterfaceIpState(
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
|
||||
|
||||
|
|
@ -3100,7 +3143,7 @@ public class TetheringTest {
|
|||
}
|
||||
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
||||
|
||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
|
||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME, false);
|
||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||
verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
|
||||
verify(mNetd, times(1)).tetherStartWithConfiguration(any());
|
||||
|
|
@ -4119,13 +4162,22 @@ public class TetheringTest {
|
|||
&& assertContainsFlag(cfg.flags, INetd.IF_STATE_UP)));
|
||||
}
|
||||
verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
|
||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||
anyString(), anyString());
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||
anyString(), anyString());
|
||||
}
|
||||
verify(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
|
||||
verify(mNetd).tetherStartWithConfiguration(any());
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||
anyString(), anyString());
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||
} else {
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||
anyString(), anyString());
|
||||
}
|
||||
verifyNoMoreInteractions(mNetd);
|
||||
reset(mNetd);
|
||||
}
|
||||
|
|
@ -4140,7 +4192,11 @@ public class TetheringTest {
|
|||
private void verifyNetdCommandForBtTearDown() throws Exception {
|
||||
verify(mNetd).tetherApplyDnsInterfaces();
|
||||
verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
|
||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||
} else {
|
||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||
}
|
||||
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
||||
// happen after T. Before T, the interface configuration control in bluetooth side.
|
||||
verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||
|
|
@ -4405,7 +4461,7 @@ public class TetheringTest {
|
|||
initTetheringOnTestThread();
|
||||
// Enable wifi P2P.
|
||||
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
|
||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME, false);
|
||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
|
||||
verify(mUpstreamNetworkMonitor).startObserveUpstreamNetworks();
|
||||
|
|
|
|||
Loading…
Reference in a new issue