blob: 1e30283a9e6c87fa09bbf09f37eb4dcfec48146a [file] [log] [blame]
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +09001/*
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
17package android.net;
18
19import static com.android.internal.util.Preconditions.checkNotNull;
20
21import android.annotation.IntDef;
22import android.annotation.NonNull;
23import android.annotation.Nullable;
Lorenzo Colittid9a569f2021-02-05 01:10:56 +090024import android.annotation.UserIdInt;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090025import android.app.Activity;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.res.Resources;
30import android.os.RemoteException;
31
Lorenzo Colittid9a569f2021-02-05 01:10:56 +090032import com.android.internal.net.LegacyVpnInfo;
33import com.android.internal.net.VpnConfig;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090034import com.android.internal.net.VpnProfile;
35
36import java.io.IOException;
37import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
39import java.security.GeneralSecurityException;
40
41/**
42 * This class provides an interface for apps to manage platform VPN profiles
43 *
44 * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
45 * further app intermediation. When a VPN profile is present and the app is selected as an always-on
46 * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
47 * app (unlike VpnService).
48 *
49 * <p>VPN apps using supported protocols should preferentially use this API over the {@link
50 * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
51 * the guarantee that VPN network traffic is not subjected to on-device packet interception.
52 *
53 * @see Ikev2VpnProfile
54 */
55public class VpnManager {
56 /** Type representing a lack of VPN @hide */
57 public static final int TYPE_VPN_NONE = -1;
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090058
59 /**
60 * A VPN created by an app using the {@link VpnService} API.
61 * @hide
62 */
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090063 public static final int TYPE_VPN_SERVICE = 1;
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090064
65 /**
66 * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
67 * @hide
68 */
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090069 public static final int TYPE_VPN_PLATFORM = 2;
70
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090071 /**
72 * An IPsec VPN created by the built-in LegacyVpnRunner.
73 * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead.
74 * @hide
75 */
76 @Deprecated
77 public static final int TYPE_VPN_LEGACY = 3;
78
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090079 /** @hide */
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090080 @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090081 @Retention(RetentionPolicy.SOURCE)
82 public @interface VpnType {}
83
84 @NonNull private final Context mContext;
85 @NonNull private final IConnectivityManager mService;
86
87 private static Intent getIntentForConfirmation() {
88 final Intent intent = new Intent();
89 final ComponentName componentName = ComponentName.unflattenFromString(
90 Resources.getSystem().getString(
91 com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
92 intent.setComponent(componentName);
93 return intent;
94 }
95
96 /**
97 * Create an instance of the VpnManager with the given context.
98 *
99 * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
100 * {@link Context.getSystemService()} method call.
101 *
102 * @hide
103 */
104 public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
105 mContext = checkNotNull(ctx, "missing Context");
106 mService = checkNotNull(service, "missing IConnectivityManager");
107 }
108
109 /**
110 * Install a VpnProfile configuration keyed on the calling app's package name.
111 *
112 * <p>This method returns {@code null} if user consent has already been granted, or an {@link
113 * Intent} to a system activity. If an intent is returned, the application should launch the
114 * activity using {@link Activity#startActivityForResult} to request user consent. The activity
115 * may pop up a dialog to require user action, and the result will come back via its {@link
116 * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
117 * consented, and the VPN profile can be started.
118 *
119 * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
120 * stored for this package.
121 * @return an Intent requesting user consent to start the VPN, or null if consent is not
122 * required based on privileges or previous user consent.
123 */
124 @Nullable
125 public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
126 final VpnProfile internalProfile;
127
128 try {
129 internalProfile = profile.toVpnProfile();
130 } catch (GeneralSecurityException | IOException e) {
131 // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
132 // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
133 // string as required by the VpnProfile.
134 throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
135 }
136
137 try {
138 // Profile can never be null; it either gets set, or an exception is thrown.
139 if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
140 return null;
141 }
142 } catch (RemoteException e) {
143 throw e.rethrowFromSystemServer();
144 }
145 return getIntentForConfirmation();
146 }
147
148 /**
149 * Delete the VPN profile configuration that was provisioned by the calling app
150 *
151 * @throws SecurityException if this would violate user settings
152 */
153 public void deleteProvisionedVpnProfile() {
154 try {
155 mService.deleteVpnProfile(mContext.getOpPackageName());
156 } catch (RemoteException e) {
157 throw e.rethrowFromSystemServer();
158 }
159 }
160
161 /**
162 * Request the startup of a previously provisioned VPN.
163 *
164 * @throws SecurityException exception if user or device settings prevent this VPN from being
165 * setup, or if user consent has not been granted
166 */
167 public void startProvisionedVpnProfile() {
168 try {
169 mService.startVpnProfile(mContext.getOpPackageName());
170 } catch (RemoteException e) {
171 throw e.rethrowFromSystemServer();
172 }
173 }
174
175 /** Tear down the VPN provided by the calling app (if any) */
176 public void stopProvisionedVpnProfile() {
177 try {
178 mService.stopVpnProfile(mContext.getOpPackageName());
179 } catch (RemoteException e) {
180 throw e.rethrowFromSystemServer();
181 }
182 }
Lorenzo Colittid9a569f2021-02-05 01:10:56 +0900183
184 /**
185 * Return the VPN configuration for the given user ID.
186 * @hide
187 */
188 @Nullable
189 public VpnConfig getVpnConfig(@UserIdInt int userId) {
190 try {
191 return mService.getVpnConfig(userId);
192 } catch (RemoteException e) {
193 throw e.rethrowFromSystemServer();
194 }
195 }
196
197 /**
198 * Prepare for a VPN application.
199 * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
200 * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
201 *
202 * @param oldPackage Package name of the application which currently controls VPN, which will
203 * be replaced. If there is no such application, this should should either be
204 * {@code null} or {@link VpnConfig.LEGACY_VPN}.
205 * @param newPackage Package name of the application which should gain control of VPN, or
206 * {@code null} to disable.
207 * @param userId User for whom to prepare the new VPN.
208 *
209 * @hide
210 */
211 public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
212 int userId) {
213 try {
214 return mService.prepareVpn(oldPackage, newPackage, userId);
215 } catch (RemoteException e) {
216 throw e.rethrowFromSystemServer();
217 }
218 }
219
220 /**
221 * Set whether the VPN package has the ability to launch VPNs without user intervention. This
222 * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
223 * class. If the caller is not {@code userId}, {@link
224 * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
225 *
226 * @param packageName The package for which authorization state should change.
227 * @param userId User for whom {@code packageName} is installed.
228 * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
229 * permissions should be granted. When unauthorizing an app, {@link
230 * VpnManager.TYPE_VPN_NONE} should be used.
231 * @hide
232 */
233 public void setVpnPackageAuthorization(
234 String packageName, int userId, @VpnManager.VpnType int vpnType) {
235 try {
236 mService.setVpnPackageAuthorization(packageName, userId, vpnType);
237 } catch (RemoteException e) {
238 throw e.rethrowFromSystemServer();
239 }
240 }
241
242 /**
243 * Return the legacy VPN information for the specified user ID.
244 * @hide
245 */
246 public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
247 try {
248 return mService.getLegacyVpnInfo(userId);
249 } catch (RemoteException e) {
250 throw e.rethrowFromSystemServer();
251 }
252 }
253
254 /**
255 * Starts a legacy VPN.
256 * @hide
257 */
258 public void startLegacyVpn(VpnProfile profile) {
259 try {
260 mService.startLegacyVpn(profile);
261 } catch (RemoteException e) {
262 throw e.rethrowFromSystemServer();
263 }
264 }
265
266 /**
267 * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
268 * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
269 * with a reload of its profile.
270 *
271 * <p>This method can only be called by the system UID
272 * @return a boolean indicating success
273 *
274 * @hide
275 */
276 public boolean updateLockdownVpn() {
277 try {
278 return mService.updateLockdownVpn();
279 } catch (RemoteException e) {
280 throw e.rethrowFromSystemServer();
281 }
282 }
283}