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

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

Merge "Add methods for IKEv2/IPsec test mode profiles" into rvc-dev am: 8f297693 am: 8492f014

Change-Id: I71aeaeb102b4357666d1c3238ffeb846c39ecf3c
parents 11d18d95 8492f014
Loading
Loading
Loading
Loading
+40 −5
Original line number 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 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);
        }
    }
}
+27 −5
Original line number Diff line number Diff line
@@ -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
+26 −5
Original line number Diff line number Diff line
@@ -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");
+40 −7
Original line number Diff line number Diff line
@@ -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