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