diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 84b301fc0a..c935cbf35e 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -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 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 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 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 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 expectedRoutes, List 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); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index dc3cbd2614..d659815766 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -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 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();