blob: f472ed4381d139482353500216d364beee58cbd7 [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 Colitti842075e2021-02-04 17:32:07 +090024import android.annotation.RequiresPermission;
Lorenzo Colittid9a569f2021-02-05 01:10:56 +090025import android.annotation.UserIdInt;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090026import android.app.Activity;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
30import android.content.res.Resources;
31import android.os.RemoteException;
32
Lorenzo Colittid9a569f2021-02-05 01:10:56 +090033import com.android.internal.net.LegacyVpnInfo;
34import com.android.internal.net.VpnConfig;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090035import com.android.internal.net.VpnProfile;
36
37import java.io.IOException;
38import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
40import java.security.GeneralSecurityException;
Lorenzo Colitti842075e2021-02-04 17:32:07 +090041import java.util.List;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090042
43/**
44 * This class provides an interface for apps to manage platform VPN profiles
45 *
46 * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
47 * further app intermediation. When a VPN profile is present and the app is selected as an always-on
48 * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
49 * app (unlike VpnService).
50 *
51 * <p>VPN apps using supported protocols should preferentially use this API over the {@link
52 * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
53 * the guarantee that VPN network traffic is not subjected to on-device packet interception.
54 *
55 * @see Ikev2VpnProfile
56 */
57public class VpnManager {
58 /** Type representing a lack of VPN @hide */
59 public static final int TYPE_VPN_NONE = -1;
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090060
61 /**
62 * A VPN created by an app using the {@link VpnService} API.
63 * @hide
64 */
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090065 public static final int TYPE_VPN_SERVICE = 1;
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090066
67 /**
68 * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
69 * @hide
70 */
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090071 public static final int TYPE_VPN_PLATFORM = 2;
72
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090073 /**
74 * An IPsec VPN created by the built-in LegacyVpnRunner.
75 * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead.
76 * @hide
77 */
78 @Deprecated
79 public static final int TYPE_VPN_LEGACY = 3;
80
Lorenzo Colittid97b4042020-11-09 10:34:03 +090081 /**
82 * Channel for VPN notifications.
83 * @hide
84 */
85 public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
86
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090087 /** @hide */
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090088 @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090089 @Retention(RetentionPolicy.SOURCE)
90 public @interface VpnType {}
91
92 @NonNull private final Context mContext;
Lorenzo Colitti842075e2021-02-04 17:32:07 +090093 @NonNull private final IVpnManager mService;
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090094
95 private static Intent getIntentForConfirmation() {
96 final Intent intent = new Intent();
97 final ComponentName componentName = ComponentName.unflattenFromString(
98 Resources.getSystem().getString(
99 com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
100 intent.setComponent(componentName);
101 return intent;
102 }
103
104 /**
105 * Create an instance of the VpnManager with the given context.
106 *
107 * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
108 * {@link Context.getSystemService()} method call.
109 *
110 * @hide
111 */
Lorenzo Colitti842075e2021-02-04 17:32:07 +0900112 public VpnManager(@NonNull Context ctx, @NonNull IVpnManager service) {
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +0900113 mContext = checkNotNull(ctx, "missing Context");
Lorenzo Colitti842075e2021-02-04 17:32:07 +0900114 mService = checkNotNull(service, "missing IVpnManager");
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +0900115 }
116
117 /**
118 * Install a VpnProfile configuration keyed on the calling app's package name.
119 *
120 * <p>This method returns {@code null} if user consent has already been granted, or an {@link
121 * Intent} to a system activity. If an intent is returned, the application should launch the
122 * activity using {@link Activity#startActivityForResult} to request user consent. The activity
123 * may pop up a dialog to require user action, and the result will come back via its {@link
124 * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
125 * consented, and the VPN profile can be started.
126 *
127 * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
128 * stored for this package.
129 * @return an Intent requesting user consent to start the VPN, or null if consent is not
130 * required based on privileges or previous user consent.
131 */
132 @Nullable
133 public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
134 final VpnProfile internalProfile;
135
136 try {
137 internalProfile = profile.toVpnProfile();
138 } catch (GeneralSecurityException | IOException e) {
139 // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
140 // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
141 // string as required by the VpnProfile.
142 throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
143 }
144
145 try {
146 // Profile can never be null; it either gets set, or an exception is thrown.
147 if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
148 return null;
149 }
150 } catch (RemoteException e) {
151 throw e.rethrowFromSystemServer();
152 }
153 return getIntentForConfirmation();
154 }
155
156 /**
157 * Delete the VPN profile configuration that was provisioned by the calling app
158 *
159 * @throws SecurityException if this would violate user settings
160 */
161 public void deleteProvisionedVpnProfile() {
162 try {
163 mService.deleteVpnProfile(mContext.getOpPackageName());
164 } catch (RemoteException e) {
165 throw e.rethrowFromSystemServer();
166 }
167 }
168
169 /**
170 * Request the startup of a previously provisioned VPN.
171 *
172 * @throws SecurityException exception if user or device settings prevent this VPN from being
173 * setup, or if user consent has not been granted
174 */
175 public void startProvisionedVpnProfile() {
176 try {
177 mService.startVpnProfile(mContext.getOpPackageName());
178 } catch (RemoteException e) {
179 throw e.rethrowFromSystemServer();
180 }
181 }
182
183 /** Tear down the VPN provided by the calling app (if any) */
184 public void stopProvisionedVpnProfile() {
185 try {
186 mService.stopVpnProfile(mContext.getOpPackageName());
187 } catch (RemoteException e) {
188 throw e.rethrowFromSystemServer();
189 }
190 }
Lorenzo Colittid9a569f2021-02-05 01:10:56 +0900191
192 /**
193 * Return the VPN configuration for the given user ID.
194 * @hide
195 */
196 @Nullable
197 public VpnConfig getVpnConfig(@UserIdInt int userId) {
198 try {
199 return mService.getVpnConfig(userId);
200 } catch (RemoteException e) {
201 throw e.rethrowFromSystemServer();
202 }
203 }
204
205 /**
Lorenzo Colitti842075e2021-02-04 17:32:07 +0900206 * Resets all VPN settings back to factory defaults.
207 * @hide
208 */
209 @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
210 public void factoryReset() {
211 try {
212 mService.factoryReset();
213 } catch (RemoteException e) {
214 throw e.rethrowFromSystemServer();
215 }
216 }
217
218 /**
Lorenzo Colittid9a569f2021-02-05 01:10:56 +0900219 * Prepare for a VPN application.
220 * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
221 * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
222 *
223 * @param oldPackage Package name of the application which currently controls VPN, which will
224 * be replaced. If there is no such application, this should should either be
225 * {@code null} or {@link VpnConfig.LEGACY_VPN}.
226 * @param newPackage Package name of the application which should gain control of VPN, or
227 * {@code null} to disable.
228 * @param userId User for whom to prepare the new VPN.
229 *
230 * @hide
231 */
232 public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
233 int userId) {
234 try {
235 return mService.prepareVpn(oldPackage, newPackage, userId);
236 } catch (RemoteException e) {
237 throw e.rethrowFromSystemServer();
238 }
239 }
240
241 /**
242 * Set whether the VPN package has the ability to launch VPNs without user intervention. This
243 * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
244 * class. If the caller is not {@code userId}, {@link
245 * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
246 *
247 * @param packageName The package for which authorization state should change.
248 * @param userId User for whom {@code packageName} is installed.
249 * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
250 * permissions should be granted. When unauthorizing an app, {@link
251 * VpnManager.TYPE_VPN_NONE} should be used.
252 * @hide
253 */
254 public void setVpnPackageAuthorization(
255 String packageName, int userId, @VpnManager.VpnType int vpnType) {
256 try {
257 mService.setVpnPackageAuthorization(packageName, userId, vpnType);
258 } catch (RemoteException e) {
259 throw e.rethrowFromSystemServer();
260 }
261 }
262
263 /**
Lorenzo Colitti842075e2021-02-04 17:32:07 +0900264 * Checks if a VPN app supports always-on mode.
265 *
266 * In order to support the always-on feature, an app has to
267 * <ul>
268 * <li>target {@link VERSION_CODES#N API 24} or above, and
269 * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
270 * meta-data field.
271 * </ul>
272 *
273 * @param userId The identifier of the user for whom the VPN app is installed.
274 * @param vpnPackage The canonical package name of the VPN app.
275 * @return {@code true} if and only if the VPN app exists and supports always-on mode.
276 * @hide
277 */
278 public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
279 try {
280 return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
281 } catch (RemoteException e) {
282 throw e.rethrowFromSystemServer();
283 }
284 }
285
286 /**
287 * Configures an always-on VPN connection through a specific application.
288 * This connection is automatically granted and persisted after a reboot.
289 *
290 * <p>The designated package should declare a {@link VpnService} in its
291 * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
292 * otherwise the call will fail.
293 *
294 * @param userId The identifier of the user to set an always-on VPN for.
295 * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
296 * to remove an existing always-on VPN configuration.
297 * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
298 * {@code false} otherwise.
299 * @param lockdownAllowlist The list of packages that are allowed to access network directly
300 * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
301 * this method must be called when a package that should be allowed is installed or
302 * uninstalled.
303 * @return {@code true} if the package is set as always-on VPN controller;
304 * {@code false} otherwise.
305 * @hide
306 */
307 @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
308 public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
309 boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
310 try {
311 return mService.setAlwaysOnVpnPackage(
312 userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
313 } catch (RemoteException e) {
314 throw e.rethrowFromSystemServer();
315 }
316 }
317
318 /**
319 * Returns the package name of the currently set always-on VPN application.
320 * If there is no always-on VPN set, or the VPN is provided by the system instead
321 * of by an app, {@code null} will be returned.
322 *
323 * @return Package name of VPN controller responsible for always-on VPN,
324 * or {@code null} if none is set.
325 * @hide
326 */
327 @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
328 public String getAlwaysOnVpnPackageForUser(int userId) {
329 try {
330 return mService.getAlwaysOnVpnPackage(userId);
331 } catch (RemoteException e) {
332 throw e.rethrowFromSystemServer();
333 }
334 }
335
336 /**
337 * @return whether always-on VPN is in lockdown mode.
338 *
339 * @hide
340 **/
341 @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
342 public boolean isVpnLockdownEnabled(int userId) {
343 try {
344 return mService.isVpnLockdownEnabled(userId);
345 } catch (RemoteException e) {
346 throw e.rethrowFromSystemServer();
347 }
348 }
349
350 /**
351 * @return the list of packages that are allowed to access network when always-on VPN is in
352 * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
353 *
354 * @hide
355 **/
356 @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
357 public List<String> getVpnLockdownAllowlist(int userId) {
358 try {
359 return mService.getVpnLockdownAllowlist(userId);
360 } catch (RemoteException e) {
361 throw e.rethrowFromSystemServer();
362 }
363 }
364
365 /**
Lorenzo Colittid9a569f2021-02-05 01:10:56 +0900366 * Return the legacy VPN information for the specified user ID.
367 * @hide
368 */
369 public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
370 try {
371 return mService.getLegacyVpnInfo(userId);
372 } catch (RemoteException e) {
373 throw e.rethrowFromSystemServer();
374 }
375 }
376
377 /**
378 * Starts a legacy VPN.
379 * @hide
380 */
381 public void startLegacyVpn(VpnProfile profile) {
382 try {
383 mService.startLegacyVpn(profile);
384 } catch (RemoteException e) {
385 throw e.rethrowFromSystemServer();
386 }
387 }
388
389 /**
390 * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
391 * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
392 * with a reload of its profile.
393 *
394 * <p>This method can only be called by the system UID
395 * @return a boolean indicating success
396 *
397 * @hide
398 */
399 public boolean updateLockdownVpn() {
400 try {
401 return mService.updateLockdownVpn();
402 } catch (RemoteException e) {
403 throw e.rethrowFromSystemServer();
404 }
405 }
406}