From bf75afdcd1fa7b0ff6fb16dedee1d1c82c411cd2 Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Wed, 27 Aug 2025 03:39:05 -0700 Subject: [PATCH] Tethering: Enable TETHERING_AND_P2P_GO_LOCAL_AGENT for 25Q4+ This change enables the TETHERING_AND_P2P_GO_LOCAL_AGENT feature on devices running Android 25Q4 or newer, in addition to being controlled by the existing feature flag. A new SdkUtil.isAtLeast25Q4() method is introduced to check for the specific SDK version. Flag: com.android.tethering.mainline.beta.tethering_and_p2p_go_local_agent Test: atest ConnectivityCoverageTests --update-device \ --test-filter '.*Tethering.*' Test: TH Bug: 420590461 Fix: 428098654 Fix: 420592689 Change-Id: I7ef79a201116de84f2c4973c4c360fd3d1ef0c4c --- Tethering/src/android/net/ip/IpServer.java | 27 ++++++++++++++++--- .../tethering/TetheringFeatureFlags.java | 6 +++++ .../unit/src/android/net/ip/IpServerTest.java | 9 +++++-- .../networkstack/tethering/TetheringTest.java | 14 +++++++++- .../com/android/net/module/util/SdkUtil.java | 5 ++++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index efb1017c16..c52b596a4b 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -42,6 +42,7 @@ 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_AND_P2P_GO_LOCAL_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; @@ -84,10 +85,12 @@ 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; import com.android.net.module.util.RoutingCoordinatorManager; +import com.android.net.module.util.SdkUtil; import com.android.net.module.util.SharedLog; import com.android.net.module.util.SyncStateMachine.StateInfo; import com.android.net.module.util.ip.InterfaceController; @@ -215,8 +218,17 @@ public class IpServer extends StateMachineShim { public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); - /** Get whether TETHERING_AND_P2P_GO_LOCAL_AGENT feature is enabled. */ - public boolean isTetheringAndP2pGoLocalAgentEnabled() { + /** + * Get whether a tethering feature flag is enabled via chickened out. + * @param name the name of the feature. + */ + public boolean isTetheringFeatureNotChickenedOut(@NonNull Context context, + @NonNull String name) { + return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, name); + } + + /** Get whether tethering and P2P GO local agent flag is enabled via beta flags. */ + public boolean isTetheringAndP2pGoLocalAgentBetaFlagEnabled() { return Flags.tetheringAndP2pGoLocalAgent(); } @@ -377,9 +389,16 @@ public class IpServer extends StateMachineShim { mLastError = TETHER_ERROR_NO_ERROR; mServingMode = STATE_AVAILABLE; - // Tethering network agent is supported on V+, and will be adopting mainline beta program. + // Determines support for the Tethering/P2P GO local network agent. This feature is + // gated on Android V+ because it requires NET_CAPABILITY_LOCAL_NETWORK. + // For 25Q4+, it is enabled by default with a kill switch to prevent impacting + // existing devices if issues arise. For older V+ devices, rollout is controlled + // by a mainline beta flag. mSupportTetheringAndP2pGoLocalAgent = SdkLevel.isAtLeastV() - && mDeps.isTetheringAndP2pGoLocalAgentEnabled(); + && (SdkUtil.isAtLeast25Q4() + ? mDeps.isTetheringFeatureNotChickenedOut(mContext, + TETHERING_AND_P2P_GO_LOCAL_AGENT) + : mDeps.isTetheringAndP2pGoLocalAgentBetaFlagEnabled()); mInitialState = new InitialState(); mLocalHotspotState = new LocalHotspotState(); diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringFeatureFlags.java b/Tethering/src/com/android/networkstack/tethering/TetheringFeatureFlags.java index 9f450ca973..62a384246c 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringFeatureFlags.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringFeatureFlags.java @@ -59,4 +59,10 @@ public class TetheringFeatureFlags { * Disabled by default. */ public static final String TETHER_ACTIVE_SESSIONS_METRICS = "tether_active_sessions_metrics"; + + /** + * A kill switch to control whether the TETHERING_AND_P2P_GO_LOCAL_AGENT is enabled. + */ + public static final String TETHERING_AND_P2P_GO_LOCAL_AGENT = + "tethering_and_p2p_go_local_agent"; } diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 3a0a60ebfa..674b0ac398 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -40,6 +40,7 @@ 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_AND_P2P_GO_LOCAL_AGENT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -227,8 +228,12 @@ 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); - doReturn(mFeatureFlags.getOrDefault(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false)) - .when(mDependencies).isTetheringAndP2pGoLocalAgentEnabled(); + final boolean agentFeatureEnabled = mFeatureFlags.getOrDefault( + Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false); + doReturn(agentFeatureEnabled).when(mDependencies).isTetheringFeatureNotChickenedOut(any(), + eq(TETHERING_AND_P2P_GO_LOCAL_AGENT)); + doReturn(agentFeatureEnabled).when(mDependencies) + .isTetheringAndP2pGoLocalAgentBetaFlagEnabled(); if (isAtLeastV()) { when(mDependencies.makeNetworkAgent(any(), any(), anyString(), anyInt(), any())) .thenReturn(mNetworkAgent); 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 504d626d70..1421a7e149 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -88,6 +88,7 @@ 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_AND_P2P_GO_LOCAL_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; @@ -479,7 +480,18 @@ public class TetheringTest { } @Override - public boolean isTetheringAndP2pGoLocalAgentEnabled() { + public boolean isTetheringFeatureNotChickenedOut(@NonNull Context context, + @NonNull String name) { + return switch (name) { + // Use one flag for mocking to reduce test complexity. + case TETHERING_AND_P2P_GO_LOCAL_AGENT -> mFeatureFlags.getOrDefault( + Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false); + default -> throw new IllegalArgumentException("Unknown flag " + name); + }; + } + + @Override + public boolean isTetheringAndP2pGoLocalAgentBetaFlagEnabled() { return mFeatureFlags.getOrDefault(Flags.FLAG_TETHERING_AND_P2P_GO_LOCAL_AGENT, false); } } diff --git a/staticlibs/framework/com/android/net/module/util/SdkUtil.java b/staticlibs/framework/com/android/net/module/util/SdkUtil.java index 1acb156c54..278f9bd023 100644 --- a/staticlibs/framework/com/android/net/module/util/SdkUtil.java +++ b/staticlibs/framework/com/android/net/module/util/SdkUtil.java @@ -54,4 +54,9 @@ public class SdkUtil { public static boolean isAtLeast25Q2() { return SDK_INT >= 36 || (SDK_INT == 35 && "Baklava".equals(Build.VERSION.CODENAME)); } + + /** Checks if the device is running on a release version of Android 25Q4 or newer */ + public static boolean isAtLeast25Q4() { + return Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1; + } }