From fef111ce477e549555f2d624f877122ee20b38e9 Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Wed, 4 Dec 2024 13:52:35 +0800 Subject: [PATCH] Add SkipNativeNetworkCreation flag in NetworkAgentConfig This flag allows ConnectivityService to skip native network creation, deferring this responsibility to Tethering. This is necessary because: - ConnectivityService requires the NetworkAgent to be in the CONNECTED state to configure routing and invokes NetworkCallback#onAvailable once ready. - Tethering requires lower-layer interactions to be completed before notifying applications. This approach ensures proper synchronization between ConnectivityService and Tethering during network setup. Long-term, all netd operations should be handled by ConnectivityService when it supports early native network creation in the CONNECTING state. As a mid-term solution, this flag enables Tethering to manage native network creation within the IpServer. Test: atest ConnectivityCoverageTests:android.net.connectivity.android.net.NetworkAgentConfigTest Bug: 349487600 Change-Id: I84da9c74dc4d40c7cd36c6657f17a158affed689 --- .../src/android/net/NetworkAgentConfig.java | 99 ++++++++++++++----- .../android/net/NetworkAgentConfigTest.kt | 10 ++ 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java index da12a0a5ab..deaa734c27 100644 --- a/framework/src/android/net/NetworkAgentConfig.java +++ b/framework/src/android/net/NetworkAgentConfig.java @@ -272,6 +272,27 @@ public final class NetworkAgentConfig implements Parcelable { return mVpnRequiresValidation; } + /** + * Whether the native network creation should be skipped. + * + * If set, the native network and routes should be maintained by the caller. + * + * @hide + */ + private boolean mSkipNativeNetworkCreation = false; + + + /** + * @return Whether the native network creation should be skipped. + * @hide + */ + // TODO: Expose API when ready. + // @FlaggedApi(Flags.FLAG_TETHERING_NETWORK_AGENT) + // @SystemApi(client = MODULE_LIBRARIES) when ready. + public boolean shouldSkipNativeNetworkCreation() { + return mSkipNativeNetworkCreation; + } + /** @hide */ public NetworkAgentConfig() { } @@ -293,6 +314,7 @@ public final class NetworkAgentConfig implements Parcelable { mLegacyExtraInfo = nac.mLegacyExtraInfo; excludeLocalRouteVpn = nac.excludeLocalRouteVpn; mVpnRequiresValidation = nac.mVpnRequiresValidation; + mSkipNativeNetworkCreation = nac.mSkipNativeNetworkCreation; } } @@ -483,6 +505,26 @@ public final class NetworkAgentConfig implements Parcelable { return this; } + /** + * Sets the native network creation should be skipped. + * + * @return this builder, to facilitate chaining. + * @hide + */ + @NonNull + // TODO: Expose API when ready. + // @FlaggedApi(Flags.FLAG_TETHERING_NETWORK_AGENT) + // @SystemApi(client = MODULE_LIBRARIES) when ready. + public Builder setSkipNativeNetworkCreation(boolean skipNativeNetworkCreation) { + if (!SdkLevel.isAtLeastV()) { + // Local agents are supported starting on U on TVs and on V on everything else. + // Thus, only support this flag on V+. + throw new UnsupportedOperationException("Method is not supported"); + } + mConfig.mSkipNativeNetworkCreation = skipNativeNetworkCreation; + return this; + } + /** * Returns the constructed {@link NetworkAgentConfig} object. */ @@ -510,7 +552,8 @@ public final class NetworkAgentConfig implements Parcelable { && Objects.equals(legacySubTypeName, that.legacySubTypeName) && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo) && excludeLocalRouteVpn == that.excludeLocalRouteVpn - && mVpnRequiresValidation == that.mVpnRequiresValidation; + && mVpnRequiresValidation == that.mVpnRequiresValidation + && mSkipNativeNetworkCreation == that.mSkipNativeNetworkCreation; } @Override @@ -518,7 +561,8 @@ public final class NetworkAgentConfig implements Parcelable { return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated, acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId, skip464xlat, legacyType, legacySubType, legacyTypeName, legacySubTypeName, - mLegacyExtraInfo, excludeLocalRouteVpn, mVpnRequiresValidation); + mLegacyExtraInfo, excludeLocalRouteVpn, mVpnRequiresValidation, + mSkipNativeNetworkCreation); } @Override @@ -539,6 +583,7 @@ public final class NetworkAgentConfig implements Parcelable { + ", legacyExtraInfo = '" + mLegacyExtraInfo + '\'' + ", excludeLocalRouteVpn = '" + excludeLocalRouteVpn + '\'' + ", vpnRequiresValidation = '" + mVpnRequiresValidation + '\'' + + ", skipNativeNetworkCreation = '" + mSkipNativeNetworkCreation + '\'' + "}"; } @@ -563,33 +608,35 @@ public final class NetworkAgentConfig implements Parcelable { out.writeString(mLegacyExtraInfo); out.writeInt(excludeLocalRouteVpn ? 1 : 0); out.writeInt(mVpnRequiresValidation ? 1 : 0); + out.writeInt(mSkipNativeNetworkCreation ? 1 : 0); } public static final @NonNull Creator CREATOR = new Creator() { - @Override - public NetworkAgentConfig createFromParcel(Parcel in) { - NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); - networkAgentConfig.allowBypass = in.readInt() != 0; - networkAgentConfig.explicitlySelected = in.readInt() != 0; - networkAgentConfig.acceptUnvalidated = in.readInt() != 0; - networkAgentConfig.acceptPartialConnectivity = in.readInt() != 0; - networkAgentConfig.subscriberId = in.readString(); - networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0; - networkAgentConfig.skip464xlat = in.readInt() != 0; - networkAgentConfig.legacyType = in.readInt(); - networkAgentConfig.legacyTypeName = in.readString(); - networkAgentConfig.legacySubType = in.readInt(); - networkAgentConfig.legacySubTypeName = in.readString(); - networkAgentConfig.mLegacyExtraInfo = in.readString(); - networkAgentConfig.excludeLocalRouteVpn = in.readInt() != 0; - networkAgentConfig.mVpnRequiresValidation = in.readInt() != 0; - return networkAgentConfig; - } + @Override + public NetworkAgentConfig createFromParcel(Parcel in) { + NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); + networkAgentConfig.allowBypass = in.readInt() != 0; + networkAgentConfig.explicitlySelected = in.readInt() != 0; + networkAgentConfig.acceptUnvalidated = in.readInt() != 0; + networkAgentConfig.acceptPartialConnectivity = in.readInt() != 0; + networkAgentConfig.subscriberId = in.readString(); + networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0; + networkAgentConfig.skip464xlat = in.readInt() != 0; + networkAgentConfig.legacyType = in.readInt(); + networkAgentConfig.legacyTypeName = in.readString(); + networkAgentConfig.legacySubType = in.readInt(); + networkAgentConfig.legacySubTypeName = in.readString(); + networkAgentConfig.mLegacyExtraInfo = in.readString(); + networkAgentConfig.excludeLocalRouteVpn = in.readInt() != 0; + networkAgentConfig.mVpnRequiresValidation = in.readInt() != 0; + networkAgentConfig.mSkipNativeNetworkCreation = in.readInt() != 0; + return networkAgentConfig; + } - @Override - public NetworkAgentConfig[] newArray(int size) { - return new NetworkAgentConfig[size]; - } - }; + @Override + public NetworkAgentConfig[] newArray(int size) { + return new NetworkAgentConfig[size]; + } + }; } diff --git a/tests/common/java/android/net/NetworkAgentConfigTest.kt b/tests/common/java/android/net/NetworkAgentConfigTest.kt index d640a730ee..fe869f87c8 100644 --- a/tests/common/java/android/net/NetworkAgentConfigTest.kt +++ b/tests/common/java/android/net/NetworkAgentConfigTest.kt @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.modules.utils.build.SdkLevel.isAtLeastS import com.android.modules.utils.build.SdkLevel.isAtLeastT +import com.android.modules.utils.build.SdkLevel.isAtLeastV import com.android.testutils.ConnectivityModuleTest import com.android.testutils.assertParcelingIsLossless import org.junit.Assert.assertEquals @@ -47,6 +48,9 @@ class NetworkAgentConfigTest { setLocalRoutesExcludedForVpn(true) setVpnRequiresValidation(true) } + if (isAtLeastV()) { + setSkipNativeNetworkCreation(true) + } }.build() assertParcelingIsLossless(config) } @@ -71,6 +75,9 @@ class NetworkAgentConfigTest { setLocalRoutesExcludedForVpn(true) setVpnRequiresValidation(true) } + if (isAtLeastV()) { + setSkipNativeNetworkCreation(true) + } }.build() assertTrue(config.isExplicitlySelected()) @@ -79,6 +86,9 @@ class NetworkAgentConfigTest { assertFalse(config.isPartialConnectivityAcceptable()) assertTrue(config.isUnvalidatedConnectivityAcceptable()) assertEquals("TEST_NETWORK", config.getLegacyTypeName()) + if (isAtLeastV()) { + assertTrue(config.shouldSkipNativeNetworkCreation()) + } if (isAtLeastT()) { assertTrue(config.areLocalRoutesExcludedForVpn()) assertTrue(config.isVpnValidationRequired())