Merge "Add methods for IKEv2/IPsec test mode profiles" am: f44b90fdc4
Change-Id: I9ff22188a641fce6b7bd44f7619d1b825988667a
This commit is contained in:
@@ -101,6 +101,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
private final boolean mIsBypassable; // Defaults in builder
|
||||
private final boolean mIsMetered; // Defaults in builder
|
||||
private final int mMaxMtu; // Defaults in builder
|
||||
private final boolean mIsRestrictedToTestNetworks;
|
||||
|
||||
private Ikev2VpnProfile(
|
||||
int type,
|
||||
@@ -116,7 +117,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
@NonNull List<String> allowedAlgorithms,
|
||||
boolean isBypassable,
|
||||
boolean isMetered,
|
||||
int maxMtu) {
|
||||
int maxMtu,
|
||||
boolean restrictToTestNetworks) {
|
||||
super(type);
|
||||
|
||||
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
|
||||
@@ -140,6 +142,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
mIsBypassable = isBypassable;
|
||||
mIsMetered = isMetered;
|
||||
mMaxMtu = maxMtu;
|
||||
mIsRestrictedToTestNetworks = restrictToTestNetworks;
|
||||
|
||||
validate();
|
||||
}
|
||||
@@ -329,6 +332,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
return mMaxMtu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this VPN profile is restricted to test networks.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isRestrictedToTestNetworks() {
|
||||
return mIsRestrictedToTestNetworks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
@@ -345,7 +357,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
mAllowedAlgorithms,
|
||||
mIsBypassable,
|
||||
mIsMetered,
|
||||
mMaxMtu);
|
||||
mMaxMtu,
|
||||
mIsRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -368,7 +381,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
&& Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
|
||||
&& mIsBypassable == other.mIsBypassable
|
||||
&& mIsMetered == other.mIsMetered
|
||||
&& mMaxMtu == other.mMaxMtu;
|
||||
&& mMaxMtu == other.mMaxMtu
|
||||
&& mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +395,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
*/
|
||||
@NonNull
|
||||
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
|
||||
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
|
||||
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
|
||||
mIsRestrictedToTestNetworks);
|
||||
profile.type = mType;
|
||||
profile.server = mServerAddr;
|
||||
profile.ipsecIdentifier = mUserIdentity;
|
||||
@@ -449,6 +464,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
builder.setBypassable(profile.isBypassable);
|
||||
builder.setMetered(profile.isMetered);
|
||||
builder.setMaxMtu(profile.maxMtu);
|
||||
if (profile.isRestrictedToTestNetworks) {
|
||||
builder.restrictToTestNetworks();
|
||||
}
|
||||
|
||||
switch (profile.type) {
|
||||
case TYPE_IKEV2_IPSEC_USER_PASS:
|
||||
@@ -621,6 +639,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
private boolean mIsBypassable = false;
|
||||
private boolean mIsMetered = true;
|
||||
private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
|
||||
private boolean mIsRestrictedToTestNetworks = false;
|
||||
|
||||
/**
|
||||
* Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
|
||||
@@ -841,6 +860,21 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts this profile to use test networks (only).
|
||||
*
|
||||
* <p>This method is for testing only, and must not be used by apps. Calling
|
||||
* provisionVpnProfile() with a profile where test-network usage is enabled will require the
|
||||
* MANAGE_TEST_NETWORKS permission.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder restrictToTestNetworks() {
|
||||
mIsRestrictedToTestNetworks = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates, builds and provisions the VpnProfile.
|
||||
*
|
||||
@@ -862,7 +896,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
|
||||
mAllowedAlgorithms,
|
||||
mIsBypassable,
|
||||
mIsMetered,
|
||||
mMaxMtu);
|
||||
mMaxMtu,
|
||||
mIsRestrictedToTestNetworks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,13 +136,19 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
public boolean isMetered = false; // 21
|
||||
public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; // 22
|
||||
public boolean areAuthParamsInline = false; // 23
|
||||
public final boolean isRestrictedToTestNetworks; // 24
|
||||
|
||||
// Helper fields.
|
||||
@UnsupportedAppUsage
|
||||
public transient boolean saveLogin = false;
|
||||
|
||||
public VpnProfile(String key) {
|
||||
this(key, false);
|
||||
}
|
||||
|
||||
public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
|
||||
this.key = key;
|
||||
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -171,6 +177,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
isMetered = in.readBoolean();
|
||||
maxMtu = in.readInt();
|
||||
areAuthParamsInline = in.readBoolean();
|
||||
isRestrictedToTestNetworks = in.readBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,6 +227,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
out.writeBoolean(isMetered);
|
||||
out.writeInt(maxMtu);
|
||||
out.writeBoolean(areAuthParamsInline);
|
||||
out.writeBoolean(isRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,12 +245,21 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
|
||||
// Acceptable numbers of values are:
|
||||
// 14-19: Standard profile, with option for serverCert, proxy
|
||||
// 24: Standard profile with serverCert, proxy and platform-VPN parameters.
|
||||
if ((values.length < 14 || values.length > 19) && values.length != 24) {
|
||||
// 24: Standard profile with serverCert, proxy and platform-VPN parameters
|
||||
// 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
|
||||
if ((values.length < 14 || values.length > 19)
|
||||
&& values.length != 24 && values.length != 25) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VpnProfile profile = new VpnProfile(key);
|
||||
final boolean isRestrictedToTestNetworks;
|
||||
if (values.length >= 25) {
|
||||
isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]);
|
||||
} else {
|
||||
isRestrictedToTestNetworks = false;
|
||||
}
|
||||
|
||||
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks);
|
||||
profile.name = values[0];
|
||||
profile.type = Integer.parseInt(values[1]);
|
||||
if (profile.type < 0 || profile.type > TYPE_MAX) {
|
||||
@@ -283,6 +300,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
|
||||
}
|
||||
|
||||
// isRestrictedToTestNetworks (values[24]) assigned as part of the constructor
|
||||
|
||||
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
|
||||
return profile;
|
||||
} catch (Exception e) {
|
||||
@@ -330,6 +349,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
builder.append(VALUE_DELIMITER).append(isMetered);
|
||||
builder.append(VALUE_DELIMITER).append(maxMtu);
|
||||
builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
|
||||
builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
|
||||
|
||||
return builder.toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
@@ -421,7 +441,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
return Objects.hash(
|
||||
key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
|
||||
l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
|
||||
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
|
||||
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
|
||||
isRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
/** Checks VPN profiles for interior equality. */
|
||||
@@ -453,7 +474,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
|
||||
&& isBypassable == other.isBypassable
|
||||
&& isMetered == other.isMetered
|
||||
&& maxMtu == other.maxMtu
|
||||
&& areAuthParamsInline == other.areAuthParamsInline;
|
||||
&& areAuthParamsInline == other.areAuthParamsInline
|
||||
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -65,6 +65,7 @@ import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
import android.net.NetworkProvider;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.UidRange;
|
||||
import android.net.VpnManager;
|
||||
@@ -2225,12 +2226,27 @@ public class Vpn {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Explicitly use only the network that ConnectivityService thinks is the "best." In
|
||||
// other words, only ever use the currently selected default network. This does mean
|
||||
// that in both onLost() and onConnected(), any old sessions MUST be torn down. This
|
||||
// does NOT include VPNs.
|
||||
// Unless the profile is restricted to test networks, explicitly use only the network
|
||||
// that ConnectivityService thinks is the "best." In other words, only ever use the
|
||||
// currently selected default network. This does mean that in both onLost() and
|
||||
// onConnected(), any old sessions MUST be torn down. This does NOT include VPNs.
|
||||
//
|
||||
// When restricted to test networks, select any network with TRANSPORT_TEST. Since the
|
||||
// creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
|
||||
// this is considered safe.
|
||||
final ConnectivityManager cm = ConnectivityManager.from(mContext);
|
||||
cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
|
||||
final NetworkRequest req;
|
||||
|
||||
if (mProfile.isRestrictedToTestNetworks()) {
|
||||
req = new NetworkRequest.Builder()
|
||||
.clearCapabilities()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
|
||||
.build();
|
||||
} else {
|
||||
req = cm.getDefaultRequest();
|
||||
}
|
||||
|
||||
cm.requestNetwork(req, mNetworkCallback);
|
||||
}
|
||||
|
||||
private boolean isActiveNetwork(@Nullable Network network) {
|
||||
@@ -2868,6 +2884,11 @@ public class Vpn {
|
||||
verifyCallingUidAndPackage(packageName);
|
||||
enforceNotRestrictedUser();
|
||||
|
||||
if (profile.isRestrictedToTestNetworks) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS,
|
||||
"Test-mode profiles require the MANAGE_TEST_NETWORKS permission");
|
||||
}
|
||||
|
||||
final byte[] encodedProfile = profile.encode();
|
||||
if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
|
||||
throw new IllegalArgumentException("Profile too big");
|
||||
|
||||
@@ -33,7 +33,9 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/** Unit tests for {@link VpnProfile}. */
|
||||
@SmallTest
|
||||
@@ -41,6 +43,9 @@ import java.util.Arrays;
|
||||
public class VpnProfileTest {
|
||||
private static final String DUMMY_PROFILE_KEY = "Test";
|
||||
|
||||
private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
|
||||
private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
|
||||
|
||||
@Test
|
||||
public void testDefaults() throws Exception {
|
||||
final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
|
||||
@@ -67,10 +72,11 @@ public class VpnProfileTest {
|
||||
assertFalse(p.isMetered);
|
||||
assertEquals(1360, p.maxMtu);
|
||||
assertFalse(p.areAuthParamsInline);
|
||||
assertFalse(p.isRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
private VpnProfile getSampleIkev2Profile(String key) {
|
||||
final VpnProfile p = new VpnProfile(key);
|
||||
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
|
||||
|
||||
p.name = "foo";
|
||||
p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
|
||||
@@ -116,7 +122,7 @@ public class VpnProfileTest {
|
||||
|
||||
@Test
|
||||
public void testParcelUnparcel() {
|
||||
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
|
||||
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -159,14 +165,41 @@ public class VpnProfileTest {
|
||||
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
|
||||
}
|
||||
|
||||
private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
|
||||
// Sort to ensure when we remove, we can do it from greatest first.
|
||||
Arrays.sort(missingIndices);
|
||||
|
||||
final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
|
||||
final List<String> parts =
|
||||
new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
|
||||
|
||||
// Remove from back first to ensure indexing is consistent.
|
||||
for (int i = missingIndices.length - 1; i >= 0; i--) {
|
||||
parts.remove(missingIndices[i]);
|
||||
}
|
||||
|
||||
return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDecodeInvalidNumberOfValues() {
|
||||
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
|
||||
final String encoded = new String(profile.encode());
|
||||
final byte[] tooFewValues =
|
||||
encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
|
||||
final String tooFewValues =
|
||||
getEncodedDecodedIkev2ProfileMissingValues(
|
||||
ENCODED_INDEX_AUTH_PARAMS_INLINE,
|
||||
ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
|
||||
|
||||
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
|
||||
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
|
||||
final String tooFewValues =
|
||||
getEncodedDecodedIkev2ProfileMissingValues(
|
||||
ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
|
||||
|
||||
// Verify decoding without isRestrictedToTestNetworks defaults to false
|
||||
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
|
||||
assertFalse(decoded.isRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user