diff options
author | 2019-12-12 23:38:36 -0800 | |
---|---|---|
committer | 2020-01-30 18:55:07 -0800 | |
commit | dc63bcc1357ddf784a6531ed0eb6b4910182d2a0 (patch) | |
tree | 06f4ac69104ea7f9e9f6651a6ed29dfb044ebf5e | |
parent | b4b925fc18b1fca0f14cbf082e92b92746dd1029 (diff) |
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
-rw-r--r-- | core/java/android/net/VpnManager.java | 71 | ||||
-rw-r--r-- | tests/net/java/android/net/VpnManagerTest.java | 66 |
2 files changed, 111 insertions, 26 deletions
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index f95807a14f00..e60cc81bf9d2 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -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(); + } } } diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java index 655c4d118592..97551c94e2ae 100644 --- a/tests/net/java/android/net/VpnManagerTest.java +++ b/tests/net/java/android/net/VpnManagerTest.java @@ -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) { - } + mVpnManager.deleteProvisionedVpnProfile(); + verify(mMockCs).deleteVpnProfile(eq(PKG_NAME)); } @Test public void testStartProvisionedVpnProfile() throws Exception { - try { - mVpnManager.startProvisionedVpnProfile(); - } catch (UnsupportedOperationException expected) { - } + mVpnManager.startProvisionedVpnProfile(); + verify(mMockCs).startVpnProfile(eq(PKG_NAME)); } @Test public void testStopProvisionedVpnProfile() throws Exception { - try { - mVpnManager.stopProvisionedVpnProfile(); - } catch (UnsupportedOperationException expected) { - } + mVpnManager.stopProvisionedVpnProfile(); + 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(); } } |