Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4b78b72 authored by Benedict Wong's avatar Benedict Wong Committed by Automerger Merge Worker
Browse files

Merge "Add methods for IKEv2/IPsec test mode profiles" am: f44b90fd am:...

Merge "Add methods for IKEv2/IPsec test mode profiles" am: f44b90fd am: 8b0b9d8a am: d84c7754 am: faea94fc

Change-Id: I73284b878b9622992c2773b4efd9297ae71e07fc
parents 87880405 faea94fc
Loading
Loading
Loading
Loading
+40 −5
Original line number Original line Diff line number Diff line
@@ -101,6 +101,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
    private final boolean mIsBypassable; // Defaults in builder
    private final boolean mIsBypassable; // Defaults in builder
    private final boolean mIsMetered; // Defaults in builder
    private final boolean mIsMetered; // Defaults in builder
    private final int mMaxMtu; // Defaults in builder
    private final int mMaxMtu; // Defaults in builder
    private final boolean mIsRestrictedToTestNetworks;


    private Ikev2VpnProfile(
    private Ikev2VpnProfile(
            int type,
            int type,
@@ -116,7 +117,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
            @NonNull List<String> allowedAlgorithms,
            @NonNull List<String> allowedAlgorithms,
            boolean isBypassable,
            boolean isBypassable,
            boolean isMetered,
            boolean isMetered,
            int maxMtu) {
            int maxMtu,
            boolean restrictToTestNetworks) {
        super(type);
        super(type);


        checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
        checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
@@ -140,6 +142,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        mIsBypassable = isBypassable;
        mIsBypassable = isBypassable;
        mIsMetered = isMetered;
        mIsMetered = isMetered;
        mMaxMtu = maxMtu;
        mMaxMtu = maxMtu;
        mIsRestrictedToTestNetworks = restrictToTestNetworks;


        validate();
        validate();
    }
    }
@@ -329,6 +332,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        return mMaxMtu;
        return mMaxMtu;
    }
    }


    /**
     * Returns whether or not this VPN profile is restricted to test networks.
     *
     * @hide
     */
    public boolean isRestrictedToTestNetworks() {
        return mIsRestrictedToTestNetworks;
    }

    @Override
    @Override
    public int hashCode() {
    public int hashCode() {
        return Objects.hash(
        return Objects.hash(
@@ -345,7 +357,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
                mAllowedAlgorithms,
                mAllowedAlgorithms,
                mIsBypassable,
                mIsBypassable,
                mIsMetered,
                mIsMetered,
                mMaxMtu);
                mMaxMtu,
                mIsRestrictedToTestNetworks);
    }
    }


    @Override
    @Override
@@ -368,7 +381,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
                && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
                && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
                && mIsBypassable == other.mIsBypassable
                && mIsBypassable == other.mIsBypassable
                && mIsMetered == other.mIsMetered
                && mIsMetered == other.mIsMetered
                && mMaxMtu == other.mMaxMtu;
                && mMaxMtu == other.mMaxMtu
                && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
    }
    }


    /**
    /**
@@ -381,7 +395,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
     */
     */
    @NonNull
    @NonNull
    public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
    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.type = mType;
        profile.server = mServerAddr;
        profile.server = mServerAddr;
        profile.ipsecIdentifier = mUserIdentity;
        profile.ipsecIdentifier = mUserIdentity;
@@ -449,6 +464,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        builder.setBypassable(profile.isBypassable);
        builder.setBypassable(profile.isBypassable);
        builder.setMetered(profile.isMetered);
        builder.setMetered(profile.isMetered);
        builder.setMaxMtu(profile.maxMtu);
        builder.setMaxMtu(profile.maxMtu);
        if (profile.isRestrictedToTestNetworks) {
            builder.restrictToTestNetworks();
        }


        switch (profile.type) {
        switch (profile.type) {
            case TYPE_IKEV2_IPSEC_USER_PASS:
            case TYPE_IKEV2_IPSEC_USER_PASS:
@@ -621,6 +639,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        private boolean mIsBypassable = false;
        private boolean mIsBypassable = false;
        private boolean mIsMetered = true;
        private boolean mIsMetered = true;
        private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
        private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
        private boolean mIsRestrictedToTestNetworks = false;


        /**
        /**
         * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
         * 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;
            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.
         * Validates, builds and provisions the VpnProfile.
         *
         *
@@ -862,7 +896,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
                    mAllowedAlgorithms,
                    mAllowedAlgorithms,
                    mIsBypassable,
                    mIsBypassable,
                    mIsMetered,
                    mIsMetered,
                    mMaxMtu);
                    mMaxMtu,
                    mIsRestrictedToTestNetworks);
        }
        }
    }
    }
}
}
+27 −5
Original line number Original line Diff line number Diff line
@@ -136,13 +136,19 @@ public final class VpnProfile implements Cloneable, Parcelable {
    public boolean isMetered = false;                            // 21
    public boolean isMetered = false;                            // 21
    public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;      // 22
    public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;      // 22
    public boolean areAuthParamsInline = false;                  // 23
    public boolean areAuthParamsInline = false;                  // 23
    public final boolean isRestrictedToTestNetworks;             // 24


    // Helper fields.
    // Helper fields.
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    public transient boolean saveLogin = false;
    public transient boolean saveLogin = false;


    public VpnProfile(String key) {
    public VpnProfile(String key) {
        this(key, false);
    }

    public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
        this.key = key;
        this.key = key;
        this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
    }
    }


    @UnsupportedAppUsage
    @UnsupportedAppUsage
@@ -171,6 +177,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
        isMetered = in.readBoolean();
        isMetered = in.readBoolean();
        maxMtu = in.readInt();
        maxMtu = in.readInt();
        areAuthParamsInline = in.readBoolean();
        areAuthParamsInline = in.readBoolean();
        isRestrictedToTestNetworks = in.readBoolean();
    }
    }


    /**
    /**
@@ -220,6 +227,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
        out.writeBoolean(isMetered);
        out.writeBoolean(isMetered);
        out.writeInt(maxMtu);
        out.writeInt(maxMtu);
        out.writeBoolean(areAuthParamsInline);
        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);
            String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
            // Acceptable numbers of values are:
            // Acceptable numbers of values are:
            // 14-19: Standard profile, with option for serverCert, proxy
            // 14-19: Standard profile, with option for serverCert, proxy
            // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
            // 24: Standard profile with serverCert, proxy and platform-VPN parameters
            if ((values.length < 14 || values.length > 19) && values.length != 24) {
            // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
            if ((values.length < 14 || values.length > 19)
                    && values.length != 24 && values.length != 25) {
                return null;
                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.name = values[0];
            profile.type = Integer.parseInt(values[1]);
            profile.type = Integer.parseInt(values[1]);
            if (profile.type < 0 || profile.type > TYPE_MAX) {
            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]);
                profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
            }
            }


            // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor

            profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
            profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
            return profile;
            return profile;
        } catch (Exception e) {
        } 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(isMetered);
        builder.append(VALUE_DELIMITER).append(maxMtu);
        builder.append(VALUE_DELIMITER).append(maxMtu);
        builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
        builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
        builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);


        return builder.toString().getBytes(StandardCharsets.UTF_8);
        return builder.toString().getBytes(StandardCharsets.UTF_8);
    }
    }
@@ -421,7 +441,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
        return Objects.hash(
        return Objects.hash(
            key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
            key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
            l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
            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. */
    /** Checks VPN profiles for interior equality. */
@@ -453,7 +474,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
                && isBypassable == other.isBypassable
                && isBypassable == other.isBypassable
                && isMetered == other.isMetered
                && isMetered == other.isMetered
                && maxMtu == other.maxMtu
                && maxMtu == other.maxMtu
                && areAuthParamsInline == other.areAuthParamsInline;
                && areAuthParamsInline == other.areAuthParamsInline
                && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks;
    }
    }


    @NonNull
    @NonNull
+26 −5
Original line number Original line Diff line number Diff line
@@ -65,6 +65,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnManager;
@@ -2225,12 +2226,27 @@ public class Vpn {


        @Override
        @Override
        public void run() {
        public void run() {
            // Explicitly use only the network that ConnectivityService thinks is the "best." In
            // Unless the profile is restricted to test networks, explicitly use only the network
            // other words, only ever use the currently selected default network. This does mean
            // that ConnectivityService thinks is the "best." In other words, only ever use the
            // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
            // currently selected default network. This does mean that in both onLost() and
            // does NOT include VPNs.
            // 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);
            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) {
        private boolean isActiveNetwork(@Nullable Network network) {
@@ -2868,6 +2884,11 @@ public class Vpn {
        verifyCallingUidAndPackage(packageName);
        verifyCallingUidAndPackage(packageName);
        enforceNotRestrictedUser();
        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();
        final byte[] encodedProfile = profile.encode();
        if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
        if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
            throw new IllegalArgumentException("Profile too big");
            throw new IllegalArgumentException("Profile too big");
+40 −7
Original line number Original line Diff line number Diff line
@@ -33,7 +33,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.runners.JUnit4;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;


/** Unit tests for {@link VpnProfile}. */
/** Unit tests for {@link VpnProfile}. */
@SmallTest
@SmallTest
@@ -41,6 +43,9 @@ import java.util.Arrays;
public class VpnProfileTest {
public class VpnProfileTest {
    private static final String DUMMY_PROFILE_KEY = "Test";
    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
    @Test
    public void testDefaults() throws Exception {
    public void testDefaults() throws Exception {
        final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
        final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
@@ -67,10 +72,11 @@ public class VpnProfileTest {
        assertFalse(p.isMetered);
        assertFalse(p.isMetered);
        assertEquals(1360, p.maxMtu);
        assertEquals(1360, p.maxMtu);
        assertFalse(p.areAuthParamsInline);
        assertFalse(p.areAuthParamsInline);
        assertFalse(p.isRestrictedToTestNetworks);
    }
    }


    private VpnProfile getSampleIkev2Profile(String key) {
    private VpnProfile getSampleIkev2Profile(String key) {
        final VpnProfile p = new VpnProfile(key);
        final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);


        p.name = "foo";
        p.name = "foo";
        p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
        p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -116,7 +122,7 @@ public class VpnProfileTest {


    @Test
    @Test
    public void testParcelUnparcel() {
    public void testParcelUnparcel() {
        assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
        assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
    }
    }


    @Test
    @Test
@@ -159,14 +165,41 @@ public class VpnProfileTest {
        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
        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
    @Test
    public void testEncodeDecodeInvalidNumberOfValues() {
    public void testEncodeDecodeInvalidNumberOfValues() {
        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
        final String tooFewValues =
        final String encoded = new String(profile.encode());
                getEncodedDecodedIkev2ProfileMissingValues(
        final byte[] tooFewValues =
                        ENCODED_INDEX_AUTH_PARAMS_INLINE,
                encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
                        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
    @Test