blob: a65769cc728b934fbb3c317ff6276340ebea0cc8 [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
Lorenzo Colittid97b4042020-11-09 10:34:03 +090079 /**
80 * Channel for VPN notifications.
81 * @hide
82 */
83 public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
84
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090085 /** @hide */
Lorenzo Colitti026fbb82021-02-03 03:12:15 +090086 @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +090087 @Retention(RetentionPolicy.SOURCE)
88 public @interface VpnType {}
89
90 @NonNull private final Context mContext;
91 @NonNull private final IConnectivityManager mService;
92
93 private static Intent getIntentForConfirmation() {
94 final Intent intent = new Intent();
95 final ComponentName componentName = ComponentName.unflattenFromString(
96 Resources.getSystem().getString(
97 com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
98 intent.setComponent(componentName);
99 return intent;
100 }
101
102 /**
103 * Create an instance of the VpnManager with the given context.
104 *
105 * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
106 * {@link Context.getSystemService()} method call.
107 *
108 * @hide
109 */
110 public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
111 mContext = checkNotNull(ctx, "missing Context");
112 mService = checkNotNull(service, "missing IConnectivityManager");
113 }
114
115 /**
116 * Install a VpnProfile configuration keyed on the calling app's package name.
117 *
118 * <p>This method returns {@code null} if user consent has already been granted, or an {@link
119 * Intent} to a system activity. If an intent is returned, the application should launch the
120 * activity using {@link Activity#startActivityForResult} to request user consent. The activity
121 * may pop up a dialog to require user action, and the result will come back via its {@link
122 * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
123 * consented, and the VPN profile can be started.
124 *
125 * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
126 * stored for this package.
127 * @return an Intent requesting user consent to start the VPN, or null if consent is not
128 * required based on privileges or previous user consent.
129 */
130 @Nullable
131 public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
132 final VpnProfile internalProfile;
133
134 try {
135 internalProfile = profile.toVpnProfile();
136 } catch (GeneralSecurityException | IOException e) {
137 // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
138 // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
139 // string as required by the VpnProfile.
140 throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
141 }
142
143 try {
144 // Profile can never be null; it either gets set, or an exception is thrown.
145 if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
146 return null;
147 }
148 } catch (RemoteException e) {
149 throw e.rethrowFromSystemServer();
150 }
151 return getIntentForConfirmation();
152 }
153
154 /**
155 * Delete the VPN profile configuration that was provisioned by the calling app
156 *
157 * @throws SecurityException if this would violate user settings
158 */
159 public void deleteProvisionedVpnProfile() {
160 try {
161 mService.deleteVpnProfile(mContext.getOpPackageName());
162 } catch (RemoteException e) {
163 throw e.rethrowFromSystemServer();
164 }
165 }
166
167 /**
168 * Request the startup of a previously provisioned VPN.
169 *
170 * @throws SecurityException exception if user or device settings prevent this VPN from being
171 * setup, or if user consent has not been granted
172 */
173 public void startProvisionedVpnProfile() {
174 try {
175 mService.startVpnProfile(mContext.getOpPackageName());
176 } catch (RemoteException e) {
177 throw e.rethrowFromSystemServer();
178 }
179 }
180
181 /** Tear down the VPN provided by the calling app (if any) */
182 public void stopProvisionedVpnProfile() {
183 try {
184 mService.stopVpnProfile(mContext.getOpPackageName());
185 } catch (RemoteException e) {
186 throw e.rethrowFromSystemServer();
187 }
188 }
Lorenzo Colittid9a569f2021-02-05 01:10:56 +0900189
190 /**
191 * Return the VPN configuration for the given user ID.
192 * @hide
193 */
194 @Nullable
195 public VpnConfig getVpnConfig(@UserIdInt int userId) {
196 try {
197 return mService.getVpnConfig(userId);
198 } catch (RemoteException e) {
199 throw e.rethrowFromSystemServer();
200 }
201 }
202
203 /**
204 * Prepare for a VPN application.
205 * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
206 * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
207 *
208 * @param oldPackage Package name of the application which currently controls VPN, which will
209 * be replaced. If there is no such application, this should should either be
210 * {@code null} or {@link VpnConfig.LEGACY_VPN}.
211 * @param newPackage Package name of the application which should gain control of VPN, or
212 * {@code null} to disable.
213 * @param userId User for whom to prepare the new VPN.
214 *
215 * @hide
216 */
217 public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
218 int userId) {
219 try {
220 return mService.prepareVpn(oldPackage, newPackage, userId);
221 } catch (RemoteException e) {
222 throw e.rethrowFromSystemServer();
223 }
224 }
225
226 /**
227 * Set whether the VPN package has the ability to launch VPNs without user intervention. This
228 * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
229 * class. If the caller is not {@code userId}, {@link
230 * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
231 *
232 * @param packageName The package for which authorization state should change.
233 * @param userId User for whom {@code packageName} is installed.
234 * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
235 * permissions should be granted. When unauthorizing an app, {@link
236 * VpnManager.TYPE_VPN_NONE} should be used.
237 * @hide
238 */
239 public void setVpnPackageAuthorization(
240 String packageName, int userId, @VpnManager.VpnType int vpnType) {
241 try {
242 mService.setVpnPackageAuthorization(packageName, userId, vpnType);
243 } catch (RemoteException e) {
244 throw e.rethrowFromSystemServer();
245 }
246 }
247
248 /**
249 * Return the legacy VPN information for the specified user ID.
250 * @hide
251 */
252 public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
253 try {
254 return mService.getLegacyVpnInfo(userId);
255 } catch (RemoteException e) {
256 throw e.rethrowFromSystemServer();
257 }
258 }
259
260 /**
261 * Starts a legacy VPN.
262 * @hide
263 */
264 public void startLegacyVpn(VpnProfile profile) {
265 try {
266 mService.startLegacyVpn(profile);
267 } catch (RemoteException e) {
268 throw e.rethrowFromSystemServer();
269 }
270 }
271
272 /**
273 * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
274 * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
275 * with a reload of its profile.
276 *
277 * <p>This method can only be called by the system UID
278 * @return a boolean indicating success
279 *
280 * @hide
281 */
282 public boolean updateLockdownVpn() {
283 try {
284 return mService.updateLockdownVpn();
285 } catch (RemoteException e) {
286 throw e.rethrowFromSystemServer();
287 }
288 }
289}