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