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

Commit dc63bcc1 authored by Benedict Wong's avatar Benedict Wong
Browse files

Add VpnManager calls to ConnectivityService

This commit adds the relevant calls to ConnectivityService for the
VpnManager API to be functional

Bug: 144246837
Test: VpnManagerTest updated, FrameworksNetTests passing
Change-Id: I446a8595e3583a842a7f89c4f8d74526a85e311c
parent b4b925fc
Loading
Loading
Loading
Loading
+64 −7
Original line number Diff line number Diff line
@@ -20,8 +20,17 @@ import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.RemoteException;

import com.android.internal.net.VpnProfile;

import java.io.IOException;
import java.security.GeneralSecurityException;

/**
 * This class provides an interface for apps to manage platform VPN profiles
@@ -41,6 +50,15 @@ public class VpnManager {
    @NonNull private final Context mContext;
    @NonNull private final IConnectivityManager mService;

    private static Intent getIntentForConfirmation() {
        final Intent intent = new Intent();
        final ComponentName componentName = ComponentName.unflattenFromString(
                Resources.getSystem().getString(
                        com.android.internal.R.string.config_customVpnConfirmDialogComponent));
        intent.setComponent(componentName);
        return intent;
    }

    /**
     * Create an instance of the VpnManger with the given context.
     *
@@ -57,18 +75,49 @@ public class VpnManager {
    /**
     * Install a VpnProfile configuration keyed on the calling app's package name.
     *
     * @param profile the PlatformVpnProfile provided by this package. Will override any previous
     *     PlatformVpnProfile stored for this package.
     * @return an intent to request user consent if needed (null otherwise).
     * <p>This method returns {@code null} if user consent has already been granted, or an {@link
     * Intent} to a system activity. If an intent is returned, the application should launch the
     * activity using {@link Activity#startActivityForResult} to request user consent. The activity
     * may pop up a dialog to require user action, and the result will come back via its {@link
     * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
     * consented, and the VPN profile can be started.
     *
     * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
     *     stored for this package.
     * @return an Intent requesting user consent to start the VPN, or null if consent is not
     *     required based on privileges or previous user consent.
     */
    @Nullable
    public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
        throw new UnsupportedOperationException("Not yet implemented");
        final VpnProfile internalProfile;

        try {
            internalProfile = profile.toVpnProfile();
        } catch (GeneralSecurityException | IOException e) {
            // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
            // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
            // string as required by the VpnProfile.
            throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
        }

        try {
            // Profile can never be null; it either gets set, or an exception is thrown.
            if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
                return null;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return getIntentForConfirmation();
    }

    /** Delete the VPN profile configuration that was provisioned by the calling app */
    public void deleteProvisionedVpnProfile() {
        throw new UnsupportedOperationException("Not yet implemented");
        try {
            mService.deleteVpnProfile(mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
@@ -78,11 +127,19 @@ public class VpnManager {
     *     setup, or if user consent has not been granted
     */
    public void startProvisionedVpnProfile() {
        throw new UnsupportedOperationException("Not yet implemented");
        try {
            mService.startVpnProfile(mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Tear down the VPN provided by the calling app (if any) */
    public void stopProvisionedVpnProfile() {
        throw new UnsupportedOperationException("Not yet implemented");
        try {
            mService.stopVpnProfile(mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+47 −19
Original line number Diff line number Diff line
@@ -16,13 +16,21 @@

package android.net;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.test.mock.MockContext;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.net.VpnProfile;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +39,12 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VpnManagerTest {
    private static final String VPN_PROFILE_KEY = "KEY";
    private static final String PKG_NAME = "fooPackage";

    private static final String SESSION_NAME_STRING = "testSession";
    private static final String SERVER_ADDR_STRING = "1.2.3.4";
    private static final String IDENTITY_STRING = "Identity";
    private static final byte[] PSK_BYTES = "preSharedKey".getBytes();

    private IConnectivityManager mMockCs;
    private VpnManager mVpnManager;
@@ -39,7 +52,7 @@ public class VpnManagerTest {
            new MockContext() {
                @Override
                public String getOpPackageName() {
                    return "fooPackage";
                    return PKG_NAME;
                }
            };

@@ -50,34 +63,49 @@ public class VpnManagerTest {
    }

    @Test
    public void testProvisionVpnProfile() throws Exception {
        try {
            mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
        } catch (UnsupportedOperationException expected) {
    public void testProvisionVpnProfilePreconsented() throws Exception {
        final PlatformVpnProfile profile = getPlatformVpnProfile();
        when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true);

        // Expect there to be no intent returned, as consent has already been granted.
        assertNull(mVpnManager.provisionVpnProfile(profile));
        verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
    }

    @Test
    public void testProvisionVpnProfileNeedsConsent() throws Exception {
        final PlatformVpnProfile profile = getPlatformVpnProfile();
        when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);

        // Expect intent to be returned, as consent has not already been granted.
        assertNotNull(mVpnManager.provisionVpnProfile(profile));
        verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
    }

    @Test
    public void testDeleteProvisionedVpnProfile() throws Exception {
        try {
        mVpnManager.deleteProvisionedVpnProfile();
        } catch (UnsupportedOperationException expected) {
        }
        verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
    }

    @Test
    public void testStartProvisionedVpnProfile() throws Exception {
        try {
        mVpnManager.startProvisionedVpnProfile();
        } catch (UnsupportedOperationException expected) {
        }
        verify(mMockCs).startVpnProfile(eq(PKG_NAME));
    }

    @Test
    public void testStopProvisionedVpnProfile() throws Exception {
        try {
        mVpnManager.stopProvisionedVpnProfile();
        } catch (UnsupportedOperationException expected) {
        verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
    }

    private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
        return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
                .setBypassable(true)
                .setMaxMtu(1300)
                .setMetered(true)
                .setAuthPsk(PSK_BYTES)
                .build();
    }
}