Adopt mainline beta program for local network agent

Unifies the `TETHERING_LOCAL_NETWORK_AGENT` and
`WIFIP2PGO_LOCAL_NETWORK_AGENT` legacy feature flags into a
single mainline beta aconfig flag:
`tethering_and_p2p_go_local_network_agent`.

This change simplifies the logic for enabling the `NetworkAgent`
for both standard tethering (global scope) and Wi-Fi P2P Group
Owner mode (local scope). The agent is now enabled for both use
cases when the new flag is on.

The implementation in `IpServer` and associated tests have
been updated to use the new unified flag.

Flag: com.android.tethering.mainline.beta.tethering_and_p2p_go_local_agent
Bug: 420590461
Test: atest TetheringTests
Test: adb root
      adb shell device_config put com_android_tethering com.android.tethering.mainline.beta.tethering_and_p2p_go_local_agent true
      adb reboot
      atest CtsTetheringTest --test-filter '.*Agent.*'
Test: atest ConnectivityCoverageTests:android.net.ip.IpServerTest --update-device
Change-Id: I55817132c57a04b4f7b7bac61b5741f45ba16182
This commit is contained in:
Junyu Lai 2025-08-12 16:34:56 +08:00
commit dc420ccccb
9 changed files with 49 additions and 93 deletions

View file

@ -66,6 +66,7 @@ java_defaults {
"connectivity-net-module-utils-bpf",
"com.android.net.flags-aconfig-java",
"com.android.tethering.beta.test.flags-aconfig-java",
"com.android.tethering.mainline.beta.flags-aconfig-java",
"modules-utils-build",
"modules-utils-statemachine",
"networkstack-client",

View file

@ -42,8 +42,6 @@ import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
import static com.android.networkstack.tethering.TetheringConfiguration.USE_SYNC_SM;
import static com.android.networkstack.tethering.TetheringFeatureFlags.TETHERING_LOCAL_NETWORK_AGENT;
import static com.android.networkstack.tethering.TetheringFeatureFlags.WIFIP2PGO_LOCAL_NETWORK_AGENT;
import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
import static com.android.networkstack.tethering.util.TetheringUtils.getTransportTypeForTetherableType;
@ -86,7 +84,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.IIpv4PrefixRequest;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetdUtils;
@ -100,6 +97,7 @@ import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.networkstack.tethering.util.StateMachineShim;
import com.android.tethering.mainline.beta.Flags;
import java.net.Inet4Address;
import java.net.Inet6Address;
@ -217,11 +215,9 @@ public class IpServer extends StateMachineShim {
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb);
/**
* @see DeviceConfigUtils#isTetheringFeatureEnabled
*/
public boolean isFeatureEnabled(Context context, String name) {
return DeviceConfigUtils.isTetheringFeatureEnabled(context, name);
/** Get whether TETHERING_AND_P2P_GO_LOCAL_AGENT feature is enabled. */
public boolean isTetheringAndP2pGoLocalAgentEnabled() {
return Flags.tetheringAndP2pGoLocalAgent();
}
/** Create a NetworkAgent instance to be used by IpServer. */
@ -335,8 +331,8 @@ public class IpServer extends StateMachineShim {
private final Handler mHandler;
private final Context mContext;
private final boolean mSupportTetheringLocalAgent;
private final boolean mSupportWifiP2pGroupOwnerLocalAgent;
// Whether to enable tethering and Wi-Fi P2p Group Owner mode local network agent.
private final boolean mSupportTetheringAndP2pGoLocalAgent;
// This will be null if the TetheredState is not entered or feature not supported.
// This will be only accessed from the IpServer handler thread.
@ -381,11 +377,9 @@ public class IpServer extends StateMachineShim {
mLastError = TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
// Tethering network agent is supported on V+, and will be rolled out gradually.
mSupportTetheringLocalAgent = SdkLevel.isAtLeastV()
&& mDeps.isFeatureEnabled(mContext, TETHERING_LOCAL_NETWORK_AGENT);
mSupportWifiP2pGroupOwnerLocalAgent = mSupportTetheringLocalAgent
&& mDeps.isFeatureEnabled(mContext, WIFIP2PGO_LOCAL_NETWORK_AGENT);
// Tethering network agent is supported on V+, and will be adopting mainline beta program.
mSupportTetheringAndP2pGoLocalAgent = SdkLevel.isAtLeastV()
&& mDeps.isTetheringAndP2pGoLocalAgentEnabled();
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
@ -1176,8 +1170,7 @@ public class IpServer extends StateMachineShim {
@SuppressLint("NewApi")
private void startServingInterface() {
if (mSupportTetheringLocalAgent && (mSupportWifiP2pGroupOwnerLocalAgent
|| getScope() != CONNECTIVITY_SCOPE_LOCAL)) {
if (mSupportTetheringAndP2pGoLocalAgent) {
try {
mTetheringAgent = mDeps.makeNetworkAgent(mContext, Looper.myLooper(), TAG,
mInterfaceType, mLinkProperties);

View file

@ -59,17 +59,4 @@ public class TetheringFeatureFlags {
* Disabled by default.
*/
public static final String TETHER_ACTIVE_SESSIONS_METRICS = "tether_active_sessions_metrics";
/**
* A feature flag to control whether the tethering local network agent should be enabled.
* Disabled by default.
*/
public static final String TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent";
/**
* A feature flag to control whether the Wifi P2P Group Owner local network agent
* should be enabled. Disabled by default. This depends on TETHERING_LOCAL_AGENT
* and will not be enabled if that flag is off.
*/
public static final String WIFIP2PGO_LOCAL_NETWORK_AGENT = "wifip2pgo_local_network_agent";
}

View file

@ -28,6 +28,7 @@ java_defaults {
static_libs: [
"DhcpPacketLib",
"androidx.test.rules",
"com.android.tethering.mainline.beta.flags-aconfig-java",
"cts-net-utils",
"mockito-target-minus-junit4",
"net-tests-utils",

View file

@ -32,7 +32,6 @@ import static android.net.TetheringTester.buildUdpPacket;
import static android.net.TetheringTester.isExpectedIcmpPacket;
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.ICMP_ECHO;
import static android.system.OsConstants.ICMP_ECHOREPLY;
import static android.system.OsConstants.IPPROTO_UDP;
@ -63,6 +62,9 @@ import android.os.Build;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Log;
import android.util.Pair;
@ -88,13 +90,13 @@ import com.android.net.module.util.structs.UdpHeader;
import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DeviceConfigRule;
import com.android.testutils.DeviceInfoUtils;
import com.android.testutils.DumpTestUtils;
import com.android.testutils.NetworkStackModuleTest;
import com.android.testutils.PollPacketReader;
import com.android.testutils.TestableNetworkCallback;
import com.android.testutils.TestableNetworkCallback.Event;
import com.android.tethering.mainline.beta.Flags;
import org.junit.After;
import org.junit.Rule;
@ -122,9 +124,8 @@ import java.util.concurrent.TimeoutException;
public class EthernetTetheringTest extends EthernetTetheringTestBase {
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
// For manipulating feature flag before and after testing.
@Rule
public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule();
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule
public final AutoReleaseNetworkCallbackRule
mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule();
@ -217,10 +218,6 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase {
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
};
// Shamelessly copied from TetheringConfiguration.
private static final String TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent";
private static final String WIFIP2PGO_LOCAL_NETWORK_AGENT = "wifip2pgo_local_network_agent";
@After
public void tearDown() throws Exception {
super.tearDown();
@ -1252,19 +1249,18 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase {
}
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@RequiresFlagsEnabled(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@Test
public void testTetheringLocalAgent_networkCallbacks() throws Exception {
assumeMatchNonThreadLocalNetworksEnabled();
mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1");
doTestLocalAgent_networkCallbacks(CONNECTIVITY_SCOPE_GLOBAL);
}
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@RequiresFlagsEnabled(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@Test
public void testWifiP2pGroupOwnerLocalAgent_networkCallbacks() throws Exception {
assumeMatchNonThreadLocalNetworksEnabled();
mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1");
mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, WIFIP2PGO_LOCAL_NETWORK_AGENT, "1");
doTestLocalAgent_networkCallbacks(CONNECTIVITY_SCOPE_LOCAL);
}

View file

@ -40,8 +40,6 @@ 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.TetheringFeatureFlags.TETHERING_LOCAL_NETWORK_AGENT;
import static com.android.networkstack.tethering.TetheringFeatureFlags.WIFIP2PGO_LOCAL_NETWORK_AGENT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -110,6 +108,8 @@ 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 com.android.testutils.com.android.testutils.SetFeatureFlagsRule.FeatureFlag;
import com.android.tethering.mainline.beta.Flags;
import org.junit.Before;
import org.junit.Rule;
@ -227,9 +227,8 @@ 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());
doReturn(mFeatureFlags.getOrDefault(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false))
.when(mDependencies).isTetheringAndP2pGoLocalAgentEnabled();
if (isAtLeastV()) {
when(mDependencies.makeNetworkAgent(any(), any(), anyString(), anyInt(), any()))
.thenReturn(mNetworkAgent);
@ -376,7 +375,8 @@ public class IpServerTest {
}
private boolean isTetheringNetworkAgentFeatureEnabled() {
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
return isAtLeastV() && mFeatureFlags.getOrDefault(
Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false);
}
@Test
@ -1115,42 +1115,22 @@ public class IpServerTest {
return true;
}
@FeatureFlag(name = Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@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);
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, true);
}
@FeatureFlag(name = Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, enabled = false)
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT, enabled = false)
@SetFeatureFlagsRule.FeatureFlag(name = WIFIP2PGO_LOCAL_NETWORK_AGENT)
@Test
public void testTetheringNetworkAgent_tetheringAgentDisabled() throws Exception {
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_GLOBAL, false);
// Even if the flag is enabled, no wifip2p network agent if the tethering network
// agent feature is not supported.
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, 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)
@SetFeatureFlagsRule.FeatureFlag(name = WIFIP2PGO_LOCAL_NETWORK_AGENT, enabled = false)
@Test
public void testTetheringNetworkAgent_p2pGroupOwnerAgentDisabled() throws Exception {
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, false);
}
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
@SetFeatureFlagsRule.FeatureFlag(name = WIFIP2PGO_LOCAL_NETWORK_AGENT)
@Test
public void testTetheringNetworkAgent_p2pGroupOwnerAgentEnabled() throws Exception {
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, true);
}
private void doTestTetheringNetworkAgent(int scope, boolean expectAgentEnabled)
throws Exception {
initStateMachine(TETHERING_USB);
@ -1184,8 +1164,8 @@ public class IpServerTest {
}
// Verify if the registration failed, tethering can be gracefully shutdown.
@FeatureFlag(name = Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
@Test
public void testTetheringNetworkAgent_registerThrows() throws Exception {
initStateMachine(TETHERING_USB);
@ -1207,8 +1187,8 @@ public class IpServerTest {
}
// Verify if the network creation failed, tethering can be gracefully shutdown.
@FeatureFlag(name = Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
@Test
public void testTetheringNetworkAgent_netdThrows() throws Exception {
initStateMachine(TETHERING_USB);
@ -1229,8 +1209,8 @@ public class IpServerTest {
}
// Verify when IPv6 address update, set routes accordingly.
@FeatureFlag(name = Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
@Test
public void testTetheringNetworkAgent_ipv6AddressUpdate() throws Exception {
initStateMachine(TETHERING_USB);

View file

@ -88,7 +88,6 @@ import static com.android.networkstack.tethering.Tethering.UserRestrictionAction
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;
import static com.android.networkstack.tethering.TetheringFeatureFlags.TETHERING_LOCAL_NETWORK_AGENT;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@ -224,6 +223,7 @@ 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 com.android.tethering.mainline.beta.Flags;
import org.junit.After;
import org.junit.Before;
@ -479,8 +479,8 @@ public class TetheringTest {
}
@Override
public boolean isFeatureEnabled(Context context, String name) {
return mFeatureFlags.getOrDefault(name, false);
public boolean isTetheringAndP2pGoLocalAgentEnabled() {
return mFeatureFlags.getOrDefault(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false);
}
}
@ -1078,7 +1078,8 @@ public class TetheringTest {
}
private boolean isTetheringNetworkAgentFeatureEnabled() {
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
return isAtLeastV() && mFeatureFlags.getOrDefault(
Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false);
}
private void verifyStopHotpot(boolean isLocalOnly) throws Exception {

View file

@ -21,3 +21,10 @@ flag {
description: "The flag enables the developer opt-in for LNP."
bug: "421952907"
}
flag {
name: "tethering_and_p2p_go_local_agent"
namespace: "com_android_tethering"
description: "The flag enables the Tethering and WI-Fi P2p Group Owner mode local network agent."
bug: "420590461"
}

View file

@ -27,7 +27,8 @@ import android.net.NetworkRequest
import android.net.TetheringManager
import android.net.connectivity.ConnectivityCompatChanges
import android.os.Build
import android.provider.DeviceConfig
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.text.TextUtils
import androidx.test.platform.app.InstrumentationRegistry
import com.android.testutils.AutoReleaseNetworkCallbackRule
@ -39,6 +40,7 @@ import com.android.testutils.TestableNetworkCallback.Event.Available
import com.android.testutils.TestableNetworkCallback.Event.CapabilitiesChanged
import com.android.testutils.TestableNetworkCallback.Event.LinkPropertiesChanged
import com.android.testutils.runAsShell
import com.android.tethering.mainline.beta.Flags
import com.google.snippet.connectivity.Wifip2pMultiDevicesSnippet
import java.util.Random
import org.junit.Assert.assertEquals
@ -59,11 +61,8 @@ class TetheringWifiP2pTest {
@get:Rule
val networkCallbackRule = AutoReleaseNetworkCallbackRule()
companion object {
// Shamelessly copied from TetheringConfiguration.
private const val TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent"
private const val WIFIP2PGO_LOCAL_NETWORK_AGENT = "wifip2pgo_local_network_agent"
}
@get:Rule
val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
private val pm = context.packageManager!!
@ -81,6 +80,7 @@ class TetheringWifiP2pTest {
return "%09d".format(Random().nextInt(1_000_000_000))
}
@RequiresFlagsEnabled(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT)
@Test
fun testWifiP2pGoNetworkAgent_networkCallbacks() {
// Check preconditions.
@ -90,16 +90,6 @@ class TetheringWifiP2pTest {
// Wifi P2p Group Owner mode still need tethering support in order to get
// onLocalOnlyInterfacesChanged callbacks.
assumeTrue(runAsShell(TETHER_PRIVILEGED) { tm.isTetheringSupported() })
deviceConfigRule.setConfig(
DeviceConfig.NAMESPACE_TETHERING,
TETHERING_LOCAL_NETWORK_AGENT,
"1"
)
deviceConfigRule.setConfig(
DeviceConfig.NAMESPACE_TETHERING,
WIFIP2PGO_LOCAL_NETWORK_AGENT,
"1"
)
val cb = TestableNetworkCallback()
val request = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK)