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
This commit is contained in:
Junyu Lai 2024-12-04 13:52:35 +08:00
commit fef111ce47
2 changed files with 84 additions and 27 deletions

View file

@ -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<NetworkAgentConfig> CREATOR =
new Creator<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;
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];
}
};
}

View file

@ -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())