1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.Activity; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.res.Resources; 29 import android.os.RemoteException; 30 31 import com.android.internal.net.VpnProfile; 32 33 import java.io.IOException; 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.security.GeneralSecurityException; 37 38 /** 39 * This class provides an interface for apps to manage platform VPN profiles 40 * 41 * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without 42 * further app intermediation. When a VPN profile is present and the app is selected as an always-on 43 * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the 44 * app (unlike VpnService). 45 * 46 * <p>VPN apps using supported protocols should preferentially use this API over the {@link 47 * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user 48 * the guarantee that VPN network traffic is not subjected to on-device packet interception. 49 * 50 * @see Ikev2VpnProfile 51 */ 52 public class VpnManager { 53 /** Type representing a lack of VPN @hide */ 54 public static final int TYPE_VPN_NONE = -1; 55 /** VPN service type code @hide */ 56 public static final int TYPE_VPN_SERVICE = 1; 57 /** Platform VPN type code @hide */ 58 public static final int TYPE_VPN_PLATFORM = 2; 59 60 /** @hide */ 61 @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM}) 62 @Retention(RetentionPolicy.SOURCE) 63 public @interface VpnType {} 64 65 @NonNull private final Context mContext; 66 @NonNull private final IConnectivityManager mService; 67 getIntentForConfirmation()68 private static Intent getIntentForConfirmation() { 69 final Intent intent = new Intent(); 70 final ComponentName componentName = ComponentName.unflattenFromString( 71 Resources.getSystem().getString( 72 com.android.internal.R.string.config_platformVpnConfirmDialogComponent)); 73 intent.setComponent(componentName); 74 return intent; 75 } 76 77 /** 78 * Create an instance of the VpnManager with the given context. 79 * 80 * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the 81 * {@link Context.getSystemService()} method call. 82 * 83 * @hide 84 */ VpnManager(@onNull Context ctx, @NonNull IConnectivityManager service)85 public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) { 86 mContext = checkNotNull(ctx, "missing Context"); 87 mService = checkNotNull(service, "missing IConnectivityManager"); 88 } 89 90 /** 91 * Install a VpnProfile configuration keyed on the calling app's package name. 92 * 93 * <p>This method returns {@code null} if user consent has already been granted, or an {@link 94 * Intent} to a system activity. If an intent is returned, the application should launch the 95 * activity using {@link Activity#startActivityForResult} to request user consent. The activity 96 * may pop up a dialog to require user action, and the result will come back via its {@link 97 * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has 98 * consented, and the VPN profile can be started. 99 * 100 * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile 101 * stored for this package. 102 * @return an Intent requesting user consent to start the VPN, or null if consent is not 103 * required based on privileges or previous user consent. 104 */ 105 @Nullable provisionVpnProfile(@onNull PlatformVpnProfile profile)106 public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) { 107 final VpnProfile internalProfile; 108 109 try { 110 internalProfile = profile.toVpnProfile(); 111 } catch (GeneralSecurityException | IOException e) { 112 // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions 113 // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded 114 // string as required by the VpnProfile. 115 throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e); 116 } 117 118 try { 119 // Profile can never be null; it either gets set, or an exception is thrown. 120 if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) { 121 return null; 122 } 123 } catch (RemoteException e) { 124 throw e.rethrowFromSystemServer(); 125 } 126 return getIntentForConfirmation(); 127 } 128 129 /** 130 * Delete the VPN profile configuration that was provisioned by the calling app 131 * 132 * @throws SecurityException if this would violate user settings 133 */ deleteProvisionedVpnProfile()134 public void deleteProvisionedVpnProfile() { 135 try { 136 mService.deleteVpnProfile(mContext.getOpPackageName()); 137 } catch (RemoteException e) { 138 throw e.rethrowFromSystemServer(); 139 } 140 } 141 142 /** 143 * Request the startup of a previously provisioned VPN. 144 * 145 * @throws SecurityException exception if user or device settings prevent this VPN from being 146 * setup, or if user consent has not been granted 147 */ startProvisionedVpnProfile()148 public void startProvisionedVpnProfile() { 149 try { 150 mService.startVpnProfile(mContext.getOpPackageName()); 151 } catch (RemoteException e) { 152 throw e.rethrowFromSystemServer(); 153 } 154 } 155 156 /** Tear down the VPN provided by the calling app (if any) */ stopProvisionedVpnProfile()157 public void stopProvisionedVpnProfile() { 158 try { 159 mService.stopVpnProfile(mContext.getOpPackageName()); 160 } catch (RemoteException e) { 161 throw e.rethrowFromSystemServer(); 162 } 163 } 164 } 165