Merge "Add and implement API for VpnManagers to request validation"

This commit is contained in:
Jean Chalard
2022-02-09 06:03:45 +00:00
committed by Gerrit Code Review
4 changed files with 88 additions and 8 deletions

View File

@@ -124,6 +124,7 @@ package android.net {
public final class NetworkAgentConfig implements android.os.Parcelable {
method @Nullable public String getSubscriberId();
method public boolean getVpnRequiresValidation();
method public boolean isBypassableVpn();
}
@@ -131,6 +132,7 @@ package android.net {
method @NonNull public android.net.NetworkAgentConfig.Builder setBypassableVpn(boolean);
method @NonNull public android.net.NetworkAgentConfig.Builder setExcludeLocalRoutesVpn(boolean);
method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
method @NonNull public android.net.NetworkAgentConfig.Builder setVpnRequiresValidation(boolean);
}
public final class NetworkCapabilities implements android.os.Parcelable {

View File

@@ -34,6 +34,8 @@ import java.util.Objects;
*/
@SystemApi
public final class NetworkAgentConfig implements Parcelable {
// TODO : make this object immutable. The fields that should stay mutable should likely
// migrate to NetworkAgentInfo.
/**
* If the {@link Network} is a VPN, whether apps are allowed to bypass the
@@ -246,6 +248,27 @@ public final class NetworkAgentConfig implements Parcelable {
return excludeLocalRouteVpn;
}
/**
* Whether network validation should be performed for this VPN network.
* {@see #getVpnRequiresValidation}
* @hide
*/
private boolean mVpnRequiresValidation = false;
/**
* Whether network validation should be performed for this VPN network.
*
* If this network isn't a VPN this should always be {@code false}, and will be ignored
* if set.
* If this network is a VPN, false means this network should always be considered validated;
* true means it follows the same validation semantics as general internet networks.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public boolean getVpnRequiresValidation() {
return mVpnRequiresValidation;
}
/** @hide */
public NetworkAgentConfig() {
}
@@ -266,6 +289,7 @@ public final class NetworkAgentConfig implements Parcelable {
legacySubTypeName = nac.legacySubTypeName;
mLegacyExtraInfo = nac.mLegacyExtraInfo;
excludeLocalRouteVpn = nac.excludeLocalRouteVpn;
mVpnRequiresValidation = nac.mVpnRequiresValidation;
}
}
@@ -408,6 +432,25 @@ public final class NetworkAgentConfig implements Parcelable {
return this;
}
/**
* Sets whether network validation should be performed for this VPN network.
*
* Only agents registering a VPN network should use this setter. On other network
* types it will be ignored.
* False means this network should always be considered validated;
* true means it follows the same validation semantics as general internet.
*
* @param vpnRequiresValidation whether this VPN requires validation.
* Default is {@code false}.
* @hide
*/
@NonNull
@SystemApi(client = MODULE_LIBRARIES)
public Builder setVpnRequiresValidation(boolean vpnRequiresValidation) {
mConfig.mVpnRequiresValidation = vpnRequiresValidation;
return this;
}
/**
* Sets whether the apps can bypass the VPN connection.
*
@@ -458,14 +501,16 @@ public final class NetworkAgentConfig implements Parcelable {
&& Objects.equals(subscriberId, that.subscriberId)
&& Objects.equals(legacyTypeName, that.legacyTypeName)
&& Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo)
&& excludeLocalRouteVpn == that.excludeLocalRouteVpn;
&& excludeLocalRouteVpn == that.excludeLocalRouteVpn
&& mVpnRequiresValidation == that.mVpnRequiresValidation;
}
@Override
public int hashCode() {
return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated,
acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId,
skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo, excludeLocalRouteVpn);
skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo, excludeLocalRouteVpn,
mVpnRequiresValidation);
}
@Override
@@ -483,6 +528,7 @@ public final class NetworkAgentConfig implements Parcelable {
+ ", legacyTypeName = '" + legacyTypeName + '\''
+ ", legacyExtraInfo = '" + mLegacyExtraInfo + '\''
+ ", excludeLocalRouteVpn = '" + excludeLocalRouteVpn + '\''
+ ", vpnRequiresValidation = '" + mVpnRequiresValidation + '\''
+ "}";
}
@@ -506,6 +552,7 @@ public final class NetworkAgentConfig implements Parcelable {
out.writeString(legacySubTypeName);
out.writeString(mLegacyExtraInfo);
out.writeInt(excludeLocalRouteVpn ? 1 : 0);
out.writeInt(mVpnRequiresValidation ? 1 : 0);
}
public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
@@ -526,6 +573,7 @@ public final class NetworkAgentConfig implements Parcelable {
networkAgentConfig.legacySubTypeName = in.readString();
networkAgentConfig.mLegacyExtraInfo = in.readString();
networkAgentConfig.excludeLocalRouteVpn = in.readInt() != 0;
networkAgentConfig.mVpnRequiresValidation = in.readInt() != 0;
return networkAgentConfig;
}

View File

@@ -20,6 +20,7 @@ import android.os.Build
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.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -49,6 +50,10 @@ class NetworkAgentConfigTest {
if (isAtLeastS()) {
setBypassableVpn(true)
}
if (isAtLeastT()) {
setExcludeLocalRoutesVpn(true)
setVpnRequiresValidation(true)
}
}.build()
assertParcelingIsLossless(config)
}
@@ -69,6 +74,10 @@ class NetworkAgentConfigTest {
setProvisioningNotificationEnabled(false)
setBypassableVpn(true)
}
if (isAtLeastT()) {
setExcludeLocalRoutesVpn(true)
setVpnRequiresValidation(true)
}
}.build()
assertTrue(config.isExplicitlySelected())
@@ -77,6 +86,10 @@ class NetworkAgentConfigTest {
assertFalse(config.isPartialConnectivityAcceptable())
assertTrue(config.isUnvalidatedConnectivityAcceptable())
assertEquals("TEST_NETWORK", config.getLegacyTypeName())
if (isAtLeastT()) {
assertTrue(config.getExcludeLocalRouteVpn())
assertTrue(config.getVpnRequiresValidation())
}
if (isAtLeastS()) {
assertEquals(testExtraInfo, config.getLegacyExtraInfo())
assertFalse(config.isNat64DetectionEnabled())

View File

@@ -50,6 +50,7 @@ public class VpnProfileTest {
private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
private static final int ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE = 25;
private static final int ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION = 26;
@Test
public void testDefaults() throws Exception {
@@ -78,10 +79,13 @@ public class VpnProfileTest {
assertEquals(1360, p.maxMtu);
assertFalse(p.areAuthParamsInline);
assertFalse(p.isRestrictedToTestNetworks);
assertFalse(p.excludeLocalRoutes);
assertFalse(p.requiresInternetValidation);
}
private VpnProfile getSampleIkev2Profile(String key) {
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
false /* excludesLocalRoutes */, true /* requiresPlatformValidation */);
p.name = "foo";
p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -129,8 +133,8 @@ public class VpnProfileTest {
@Test
public void testParcelUnparcel() {
if (isAtLeastT()) {
// excludeLocalRoutes is added in T.
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 24);
// excludeLocalRoutes, requiresPlatformValidation were added in T.
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 25);
} else {
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
}
@@ -174,7 +178,8 @@ public class VpnProfileTest {
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_AUTH_PARAMS_INLINE,
ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION /* missingIndices */);
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
}
@@ -194,13 +199,25 @@ public class VpnProfileTest {
public void testEncodeDecodeMissingExcludeLocalRoutes() {
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION /* missingIndices */);
// Verify decoding without isRestrictedToTestNetworks defaults to false
// Verify decoding without excludeLocalRoutes defaults to false
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
assertFalse(decoded.excludeLocalRoutes);
}
@Test
public void testEncodeDecodeMissingRequiresValidation() {
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION /* missingIndices */);
// Verify decoding without requiresValidation defaults to false
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
assertFalse(decoded.requiresInternetValidation);
}
@Test
public void testEncodeDecodeLoginsNotSaved() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);