• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 com.android.vpndialogs;
18 
19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
20 
21 import android.content.DialogInterface;
22 import android.content.pm.PackageManager;
23 import android.graphics.drawable.Drawable;
24 import android.net.VpnManager;
25 import android.os.Bundle;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 import android.text.Html;
29 import android.text.Html.ImageGetter;
30 import android.util.Log;
31 import android.util.TypedValue;
32 import android.view.View;
33 import android.widget.Button;
34 import android.widget.TextView;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.app.AlertActivity;
38 import com.android.internal.net.VpnConfig;
39 
40 public class ConfirmDialog extends AlertActivity
41         implements DialogInterface.OnClickListener, ImageGetter {
42     private static final String TAG = "VpnConfirm";
43 
44     // Usually the label represents the app name, 150 code points might be enough to display the app
45     // name, and 150 code points won't cover the warning message from VpnDialog.
46     @VisibleForTesting
47     static final int MAX_VPN_LABEL_LENGTH = 150;
48 
49     @VpnManager.VpnType private final int mVpnType;
50 
51     private String mPackage;
52 
53     private VpnManager mVm;
54 
55     private View mView;
56 
ConfirmDialog()57     public ConfirmDialog() {
58         this(VpnManager.TYPE_VPN_SERVICE);
59     }
60 
ConfirmDialog(@pnManager.VpnType int vpnType)61     public ConfirmDialog(@VpnManager.VpnType int vpnType) {
62         mVpnType = vpnType;
63     }
64 
65     /**
66      * This function will use the string resource to combine the VPN label and the package name.
67      *
68      * If the VPN label violates the length restriction, the first 30 code points of VPN label and
69      * the package name will be returned. Or return the VPN label and the package name directly if
70      * the VPN label doesn't violate the length restriction.
71      *
72      * The result will be something like,
73      * - ThisIsAVeryLongVpnAppNameWhich... (com.vpn.app)
74      *   if the VPN label violates the length restriction.
75      * or
76      * - VpnLabelWith<br>HtmlTag (com.vpn.app)
77      *   if the VPN label doesn't violate the length restriction.
78      *
79      */
getSimplifiedLabel(String vpnLabel, String packageName)80     private String getSimplifiedLabel(String vpnLabel, String packageName) {
81         if (vpnLabel.codePointCount(0, vpnLabel.length()) > 30) {
82             return getString(R.string.sanitized_vpn_label_with_ellipsis,
83                     vpnLabel.substring(0, vpnLabel.offsetByCodePoints(0, 30)),
84                             packageName);
85         }
86 
87         return getString(R.string.sanitized_vpn_label, vpnLabel, packageName);
88     }
89 
90     @VisibleForTesting
getSanitizedVpnLabel(String vpnLabel, String packageName)91     protected String getSanitizedVpnLabel(String vpnLabel, String packageName) {
92         final String sanitizedVpnLabel = Html.escapeHtml(vpnLabel);
93         final boolean exceedMaxVpnLabelLength = sanitizedVpnLabel.codePointCount(0,
94                 sanitizedVpnLabel.length()) > MAX_VPN_LABEL_LENGTH;
95         if (exceedMaxVpnLabelLength || !vpnLabel.equals(sanitizedVpnLabel)) {
96             return getSimplifiedLabel(sanitizedVpnLabel, packageName);
97         }
98 
99         return sanitizedVpnLabel;
100     }
101 
102     @Override
onCreate(Bundle savedInstanceState)103     protected void onCreate(Bundle savedInstanceState) {
104         super.onCreate(savedInstanceState);
105         mPackage = getCallingPackage();
106         mVm = getSystemService(VpnManager.class);
107 
108         if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
109             setResult(RESULT_OK);
110             finish();
111             return;
112         }
113         if (UserManager.get(this).hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
114             finish();
115             return;
116         }
117         final String alwaysOnVpnPackage = mVm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
118         // Can't prepare new vpn app when another vpn is always-on
119         if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
120             finish();
121             return;
122         }
123         mView = View.inflate(this, R.layout.confirm, null);
124         ((TextView) mView.findViewById(R.id.warning)).setText(
125                 Html.fromHtml(getString(R.string.warning, getSanitizedVpnLabel(
126                         getVpnLabel().toString(), mPackage)),
127                         this /* imageGetter */, null /* tagHandler */));
128         mAlertParams.mTitle = getText(R.string.prompt);
129         mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
130         mAlertParams.mPositiveButtonListener = this;
131         mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
132         mAlertParams.mView = mView;
133         setupAlert();
134 
135         getWindow().setCloseOnTouchOutside(false);
136         getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
137         Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
138         button.setFilterTouchesWhenObscured(true);
139     }
140 
141     @VisibleForTesting
getWarningText()142     public CharSequence getWarningText() {
143         return ((TextView) mView.findViewById(R.id.warning)).getText();
144     }
145 
getVpnLabel()146     private CharSequence getVpnLabel() {
147         try {
148             return VpnConfig.getVpnLabel(this, mPackage);
149         } catch (PackageManager.NameNotFoundException e) {
150             throw new IllegalStateException(e);
151         }
152     }
153 
154     @Override
getDrawable(String source)155     public Drawable getDrawable(String source) {
156         // Should only reach this when fetching the VPN icon for the warning string.
157         final Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
158         icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
159 
160         final TypedValue tv = new TypedValue();
161         if (getTheme().resolveAttribute(android.R.attr.textColorPrimary, tv, true)) {
162             icon.setTint(getColor(tv.resourceId));
163         } else {
164             Log.w(TAG, "Unable to resolve theme color");
165         }
166 
167         return icon;
168     }
169 
170     @Override
onBackPressed()171     public void onBackPressed() {
172     }
173 
174     @Override
onClick(DialogInterface dialog, int which)175     public void onClick(DialogInterface dialog, int which) {
176         try {
177             if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
178                 // Authorize this app to initiate VPN connections in the future without user
179                 // intervention.
180                 mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
181                 setResult(RESULT_OK);
182             }
183         } catch (Exception e) {
184             Log.e(TAG, "onClick", e);
185         }
186     }
187 }
188