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.settings.vpn2; 18 19 import static com.android.internal.net.VpnProfile.isLegacyType; 20 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.pm.PackageManager; 24 import android.net.ProxyInfo; 25 import android.os.Bundle; 26 import android.os.SystemProperties; 27 import android.text.Editable; 28 import android.text.TextWatcher; 29 import android.util.Log; 30 import android.view.View; 31 import android.view.WindowManager; 32 import android.widget.AdapterView; 33 import android.widget.ArrayAdapter; 34 import android.widget.CheckBox; 35 import android.widget.CompoundButton; 36 import android.widget.Spinner; 37 import android.widget.TextView; 38 39 import androidx.appcompat.app.AlertDialog; 40 41 import com.android.internal.net.VpnProfile; 42 import com.android.net.module.util.ProxyUtils; 43 import com.android.settings.R; 44 import com.android.settings.utils.AndroidKeystoreAliasLoader; 45 46 import java.net.InetAddress; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Collection; 50 import java.util.List; 51 52 /** 53 * Dialog showing information about a VPN configuration. The dialog 54 * can be launched to either edit or prompt for credentials to connect 55 * to a user-added VPN. 56 * 57 * {@see AppDialog} 58 */ 59 class ConfigDialog extends AlertDialog implements TextWatcher, 60 View.OnClickListener, AdapterView.OnItemSelectedListener, 61 CompoundButton.OnCheckedChangeListener { 62 private static final String TAG = "ConfigDialog"; 63 private final DialogInterface.OnClickListener mListener; 64 private final VpnProfile mProfile; 65 66 private boolean mEditing; 67 private boolean mExists; 68 private List<String> mTotalTypes; 69 private List<String> mAllowedTypes; 70 71 private View mView; 72 73 private TextView mName; 74 private Spinner mType; 75 private TextView mServer; 76 private TextView mUsername; 77 private TextView mPassword; 78 private TextView mSearchDomains; 79 private TextView mDnsServers; 80 private TextView mRoutes; 81 private Spinner mProxySettings; 82 private TextView mProxyHost; 83 private TextView mProxyPort; 84 private CheckBox mMppe; 85 private TextView mL2tpSecret; 86 private TextView mIpsecIdentifier; 87 private TextView mIpsecSecret; 88 private Spinner mIpsecUserCert; 89 private Spinner mIpsecCaCert; 90 private Spinner mIpsecServerCert; 91 private CheckBox mSaveLogin; 92 private CheckBox mShowOptions; 93 private CheckBox mAlwaysOnVpn; 94 private TextView mAlwaysOnInvalidReason; 95 ConfigDialog(Context context, DialogInterface.OnClickListener listener, VpnProfile profile, boolean editing, boolean exists)96 ConfigDialog(Context context, DialogInterface.OnClickListener listener, 97 VpnProfile profile, boolean editing, boolean exists) { 98 super(context); 99 100 mListener = listener; 101 mProfile = profile; 102 mEditing = editing; 103 mExists = exists; 104 } 105 106 @Override onCreate(Bundle savedState)107 protected void onCreate(Bundle savedState) { 108 mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null); 109 setView(mView); 110 111 Context context = getContext(); 112 113 // First, find out all the fields. 114 mName = (TextView) mView.findViewById(R.id.name); 115 mType = (Spinner) mView.findViewById(R.id.type); 116 mServer = (TextView) mView.findViewById(R.id.server); 117 mUsername = (TextView) mView.findViewById(R.id.username); 118 mPassword = (TextView) mView.findViewById(R.id.password); 119 mSearchDomains = (TextView) mView.findViewById(R.id.search_domains); 120 mDnsServers = (TextView) mView.findViewById(R.id.dns_servers); 121 mRoutes = (TextView) mView.findViewById(R.id.routes); 122 mProxySettings = (Spinner) mView.findViewById(R.id.vpn_proxy_settings); 123 mProxyHost = (TextView) mView.findViewById(R.id.vpn_proxy_host); 124 mProxyPort = (TextView) mView.findViewById(R.id.vpn_proxy_port); 125 mMppe = (CheckBox) mView.findViewById(R.id.mppe); 126 mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret); 127 mIpsecIdentifier = (TextView) mView.findViewById(R.id.ipsec_identifier); 128 mIpsecSecret = (TextView) mView.findViewById(R.id.ipsec_secret); 129 mIpsecUserCert = (Spinner) mView.findViewById(R.id.ipsec_user_cert); 130 mIpsecCaCert = (Spinner) mView.findViewById(R.id.ipsec_ca_cert); 131 mIpsecServerCert = (Spinner) mView.findViewById(R.id.ipsec_server_cert); 132 mSaveLogin = (CheckBox) mView.findViewById(R.id.save_login); 133 mShowOptions = (CheckBox) mView.findViewById(R.id.show_options); 134 mAlwaysOnVpn = (CheckBox) mView.findViewById(R.id.always_on_vpn); 135 mAlwaysOnInvalidReason = (TextView) mView.findViewById(R.id.always_on_invalid_reason); 136 137 // Second, copy values from the profile. 138 mName.setText(mProfile.name); 139 setTypesByFeature(mType); 140 // Not all types will be available to the user. Find the index corresponding to the 141 // string of the profile's type. 142 if (mAllowedTypes != null && mTotalTypes != null) { 143 mType.setSelection(mAllowedTypes.indexOf(mTotalTypes.get(mProfile.type))); 144 } else { 145 Log.w(TAG, "Allowed or Total vpn types not initialized when setting initial selection"); 146 } 147 mServer.setText(mProfile.server); 148 if (mProfile.saveLogin) { 149 mUsername.setText(mProfile.username); 150 mPassword.setText(mProfile.password); 151 } 152 mSearchDomains.setText(mProfile.searchDomains); 153 mDnsServers.setText(mProfile.dnsServers); 154 mRoutes.setText(mProfile.routes); 155 if (mProfile.proxy != null) { 156 mProxyHost.setText(mProfile.proxy.getHost()); 157 int port = mProfile.proxy.getPort(); 158 mProxyPort.setText(port == 0 ? "" : Integer.toString(port)); 159 } 160 mMppe.setChecked(mProfile.mppe); 161 mL2tpSecret.setText(mProfile.l2tpSecret); 162 mL2tpSecret.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium); 163 mIpsecIdentifier.setText(mProfile.ipsecIdentifier); 164 mIpsecSecret.setText(mProfile.ipsecSecret); 165 final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = 166 new AndroidKeystoreAliasLoader(null); 167 loadCertificates(mIpsecUserCert, androidKeystoreAliasLoader.getKeyCertAliases(), 0, 168 mProfile.ipsecUserCert); 169 loadCertificates(mIpsecCaCert, androidKeystoreAliasLoader.getCaCertAliases(), 170 R.string.vpn_no_ca_cert, mProfile.ipsecCaCert); 171 loadCertificates(mIpsecServerCert, androidKeystoreAliasLoader.getKeyCertAliases(), 172 R.string.vpn_no_server_cert, mProfile.ipsecServerCert); 173 mSaveLogin.setChecked(mProfile.saveLogin); 174 mAlwaysOnVpn.setChecked(mProfile.key.equals(VpnUtils.getLockdownVpn())); 175 mPassword.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium); 176 177 // Hide lockdown VPN on devices that require IMS authentication 178 if (SystemProperties.getBoolean("persist.radio.imsregrequired", false)) { 179 mAlwaysOnVpn.setVisibility(View.GONE); 180 } 181 182 // Third, add listeners to required fields. 183 mName.addTextChangedListener(this); 184 mType.setOnItemSelectedListener(this); 185 mServer.addTextChangedListener(this); 186 mUsername.addTextChangedListener(this); 187 mPassword.addTextChangedListener(this); 188 mDnsServers.addTextChangedListener(this); 189 mRoutes.addTextChangedListener(this); 190 mProxySettings.setOnItemSelectedListener(this); 191 mProxyHost.addTextChangedListener(this); 192 mProxyPort.addTextChangedListener(this); 193 mIpsecIdentifier.addTextChangedListener(this); 194 mIpsecSecret.addTextChangedListener(this); 195 mIpsecUserCert.setOnItemSelectedListener(this); 196 mShowOptions.setOnClickListener(this); 197 mAlwaysOnVpn.setOnCheckedChangeListener(this); 198 199 // Fourth, determine whether to do editing or connecting. 200 mEditing = mEditing || !validate(true /*editing*/); 201 202 if (mEditing) { 203 setTitle(R.string.vpn_edit); 204 205 // Show common fields. 206 mView.findViewById(R.id.editor).setVisibility(View.VISIBLE); 207 208 // Show type-specific fields. 209 changeType(mProfile.type); 210 211 // Hide 'save login' when we are editing. 212 mSaveLogin.setVisibility(View.GONE); 213 214 configureAdvancedOptionsVisibility(); 215 216 if (mExists) { 217 // Create a button to forget the profile if it has already been saved.. 218 setButton(DialogInterface.BUTTON_NEUTRAL, 219 context.getString(R.string.vpn_forget), mListener); 220 221 // Display warning subtitle if the existing VPN is an insecure type... 222 if (VpnProfile.isLegacyType(mProfile.type)) { 223 TextView subtitle = mView.findViewById(R.id.dialog_alert_subtitle); 224 subtitle.setVisibility(View.VISIBLE); 225 } 226 } 227 228 // Create a button to save the profile. 229 setButton(DialogInterface.BUTTON_POSITIVE, 230 context.getString(R.string.vpn_save), mListener); 231 } else { 232 setTitle(context.getString(R.string.vpn_connect_to, mProfile.name)); 233 234 setUsernamePasswordVisibility(mProfile.type); 235 236 // Create a button to connect the network. 237 setButton(DialogInterface.BUTTON_POSITIVE, 238 context.getString(R.string.vpn_connect), mListener); 239 } 240 241 // Always provide a cancel button. 242 setButton(DialogInterface.BUTTON_NEGATIVE, 243 context.getString(R.string.vpn_cancel), mListener); 244 245 // Let AlertDialog create everything. 246 super.onCreate(savedState); 247 248 // Update UI controls according to the current configuration. 249 updateUiControls(); 250 251 // Workaround to resize the dialog for the input method. 252 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | 253 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 254 } 255 256 @Override onRestoreInstanceState(Bundle savedState)257 public void onRestoreInstanceState(Bundle savedState) { 258 super.onRestoreInstanceState(savedState); 259 260 // Visibility isn't restored by super.onRestoreInstanceState, so re-show the advanced 261 // options here if they were already revealed or set. 262 configureAdvancedOptionsVisibility(); 263 } 264 265 @Override afterTextChanged(Editable field)266 public void afterTextChanged(Editable field) { 267 updateUiControls(); 268 } 269 270 @Override beforeTextChanged(CharSequence s, int start, int count, int after)271 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 272 } 273 274 @Override onTextChanged(CharSequence s, int start, int before, int count)275 public void onTextChanged(CharSequence s, int start, int before, int count) { 276 } 277 278 @Override onClick(View view)279 public void onClick(View view) { 280 if (view == mShowOptions) { 281 configureAdvancedOptionsVisibility(); 282 } 283 } 284 285 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)286 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 287 if (parent == mType) { 288 // Because the spinner may not display all available types, 289 // convert the selected position into the actual vpn profile type integer. 290 final int profileType = convertAllowedIndexToProfileType(position); 291 changeType(profileType); 292 } else if (parent == mProxySettings) { 293 updateProxyFieldsVisibility(position); 294 } 295 updateUiControls(); 296 } 297 298 @Override onNothingSelected(AdapterView<?> parent)299 public void onNothingSelected(AdapterView<?> parent) { 300 } 301 302 @Override onCheckedChanged(CompoundButton compoundButton, boolean b)303 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 304 if (compoundButton == mAlwaysOnVpn) { 305 updateUiControls(); 306 } 307 } 308 isVpnAlwaysOn()309 public boolean isVpnAlwaysOn() { 310 return mAlwaysOnVpn.isChecked(); 311 } 312 313 /** 314 * Updates the UI according to the current configuration entered by the user. 315 * 316 * These include: 317 * "Always-on VPN" checkbox 318 * Reason for "Always-on VPN" being disabled, when necessary 319 * Proxy info if manually configured 320 * "Save account information" checkbox 321 * "Save" and "Connect" buttons 322 */ updateUiControls()323 private void updateUiControls() { 324 VpnProfile profile = getProfile(); 325 326 // Always-on VPN 327 if (profile.isValidLockdownProfile()) { 328 mAlwaysOnVpn.setEnabled(true); 329 mAlwaysOnInvalidReason.setVisibility(View.GONE); 330 } else { 331 mAlwaysOnVpn.setChecked(false); 332 mAlwaysOnVpn.setEnabled(false); 333 if (!profile.isTypeValidForLockdown()) { 334 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_type); 335 } else if (isLegacyType(profile.type) && !profile.isServerAddressNumeric()) { 336 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_server); 337 } else if (isLegacyType(profile.type) && !profile.hasDns()) { 338 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_no_dns); 339 } else if (isLegacyType(profile.type) && !profile.areDnsAddressesNumeric()) { 340 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_dns); 341 } else { 342 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_other); 343 } 344 mAlwaysOnInvalidReason.setVisibility(View.VISIBLE); 345 } 346 347 // Show proxy fields if any proxy field is filled. 348 if (mProfile.proxy != null && (!mProfile.proxy.getHost().isEmpty() || 349 mProfile.proxy.getPort() != 0)) { 350 mProxySettings.setSelection(VpnProfile.PROXY_MANUAL); 351 updateProxyFieldsVisibility(VpnProfile.PROXY_MANUAL); 352 } 353 354 // Save account information 355 if (mAlwaysOnVpn.isChecked()) { 356 mSaveLogin.setChecked(true); 357 mSaveLogin.setEnabled(false); 358 } else { 359 mSaveLogin.setChecked(mProfile.saveLogin); 360 mSaveLogin.setEnabled(true); 361 } 362 363 // Save or Connect button 364 getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing)); 365 } 366 updateProxyFieldsVisibility(int position)367 private void updateProxyFieldsVisibility(int position) { 368 final int visible = position == VpnProfile.PROXY_MANUAL ? View.VISIBLE : View.GONE; 369 mView.findViewById(R.id.vpn_proxy_fields).setVisibility(visible); 370 } 371 isAdvancedOptionsEnabled()372 private boolean isAdvancedOptionsEnabled() { 373 return mSearchDomains.getText().length() > 0 || mDnsServers.getText().length() > 0 || 374 mRoutes.getText().length() > 0 || mProxyHost.getText().length() > 0 375 || mProxyPort.getText().length() > 0; 376 } 377 configureAdvancedOptionsVisibility()378 private void configureAdvancedOptionsVisibility() { 379 if (mShowOptions.isChecked() || isAdvancedOptionsEnabled()) { 380 mView.findViewById(R.id.options).setVisibility(View.VISIBLE); 381 mShowOptions.setVisibility(View.GONE); 382 383 // Configure networking option visibility 384 // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes 385 final int visibility = 386 isLegacyType(getSelectedVpnType()) ? View.VISIBLE : View.GONE; 387 mView.findViewById(R.id.network_options).setVisibility(visibility); 388 } else { 389 mView.findViewById(R.id.options).setVisibility(View.GONE); 390 mShowOptions.setVisibility(View.VISIBLE); 391 } 392 } 393 changeType(int type)394 private void changeType(int type) { 395 // First, hide everything. 396 mMppe.setVisibility(View.GONE); 397 mView.findViewById(R.id.l2tp).setVisibility(View.GONE); 398 mView.findViewById(R.id.ipsec_psk).setVisibility(View.GONE); 399 mView.findViewById(R.id.ipsec_user).setVisibility(View.GONE); 400 mView.findViewById(R.id.ipsec_peer).setVisibility(View.GONE); 401 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.GONE); 402 403 setUsernamePasswordVisibility(type); 404 405 // Always enable identity for IKEv2/IPsec profiles. 406 if (!isLegacyType(type)) { 407 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE); 408 } 409 410 // Then, unhide type-specific fields. 411 switch (type) { 412 case VpnProfile.TYPE_PPTP: 413 mMppe.setVisibility(View.VISIBLE); 414 break; 415 416 case VpnProfile.TYPE_L2TP_IPSEC_PSK: 417 mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE); 418 // fall through 419 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through 420 case VpnProfile.TYPE_IPSEC_XAUTH_PSK: 421 mView.findViewById(R.id.ipsec_psk).setVisibility(View.VISIBLE); 422 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE); 423 break; 424 425 case VpnProfile.TYPE_L2TP_IPSEC_RSA: 426 mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE); 427 // fall through 428 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through 429 case VpnProfile.TYPE_IPSEC_XAUTH_RSA: 430 mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE); 431 // fall through 432 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through 433 case VpnProfile.TYPE_IPSEC_HYBRID_RSA: 434 mView.findViewById(R.id.ipsec_peer).setVisibility(View.VISIBLE); 435 break; 436 } 437 438 configureAdvancedOptionsVisibility(); 439 } 440 validate(boolean editing)441 private boolean validate(boolean editing) { 442 if (mAlwaysOnVpn.isChecked() && !getProfile().isValidLockdownProfile()) { 443 return false; 444 } 445 446 final int type = getSelectedVpnType(); 447 if (!editing && requiresUsernamePassword(type)) { 448 return mUsername.getText().length() != 0 && mPassword.getText().length() != 0; 449 } 450 if (mName.getText().length() == 0 || mServer.getText().length() == 0) { 451 return false; 452 } 453 454 // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes 455 if (isLegacyType(mProfile.type) 456 && (!validateAddresses(mDnsServers.getText().toString(), false) 457 || !validateAddresses(mRoutes.getText().toString(), true))) { 458 return false; 459 } 460 461 // All IKEv2 methods require an identifier 462 if (!isLegacyType(mProfile.type) && mIpsecIdentifier.getText().length() == 0) { 463 return false; 464 } 465 466 if (!validateProxy()) { 467 return false; 468 } 469 470 switch (type) { 471 case VpnProfile.TYPE_PPTP: // fall through 472 case VpnProfile.TYPE_IPSEC_HYBRID_RSA: // fall through 473 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: 474 return true; 475 476 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through 477 case VpnProfile.TYPE_L2TP_IPSEC_PSK: // fall through 478 case VpnProfile.TYPE_IPSEC_XAUTH_PSK: 479 return mIpsecSecret.getText().length() != 0; 480 481 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through 482 case VpnProfile.TYPE_L2TP_IPSEC_RSA: // fall through 483 case VpnProfile.TYPE_IPSEC_XAUTH_RSA: 484 return mIpsecUserCert.getSelectedItemPosition() != 0; 485 } 486 return false; 487 } 488 validateAddresses(String addresses, boolean cidr)489 private boolean validateAddresses(String addresses, boolean cidr) { 490 try { 491 for (String address : addresses.split(" ")) { 492 if (address.isEmpty()) { 493 continue; 494 } 495 // Legacy VPN currently only supports IPv4. 496 int prefixLength = 32; 497 if (cidr) { 498 String[] parts = address.split("/", 2); 499 address = parts[0]; 500 prefixLength = Integer.parseInt(parts[1]); 501 } 502 byte[] bytes = InetAddress.parseNumericAddress(address).getAddress(); 503 int integer = (bytes[3] & 0xFF) | (bytes[2] & 0xFF) << 8 | 504 (bytes[1] & 0xFF) << 16 | (bytes[0] & 0xFF) << 24; 505 if (bytes.length != 4 || prefixLength < 0 || prefixLength > 32 || 506 (prefixLength < 32 && (integer << prefixLength) != 0)) { 507 return false; 508 } 509 } 510 } catch (Exception e) { 511 return false; 512 } 513 return true; 514 } 515 setTypesByFeature(Spinner typeSpinner)516 private void setTypesByFeature(Spinner typeSpinner) { 517 String[] types = getContext().getResources().getStringArray(R.array.vpn_types); 518 mTotalTypes = new ArrayList<>(Arrays.asList(types)); 519 mAllowedTypes = new ArrayList<>(Arrays.asList(types)); 520 521 // Although FEATURE_IPSEC_TUNNELS should always be present in android S and beyond, 522 // keep this check here just to be safe. 523 if (!getContext().getPackageManager().hasSystemFeature( 524 PackageManager.FEATURE_IPSEC_TUNNELS)) { 525 Log.wtf(TAG, "FEATURE_IPSEC_TUNNELS missing from system"); 526 } 527 // If the vpn is new or is not already a legacy type, 528 // don't allow the user to set the type to a legacy option. 529 530 // Set the mProfile.type to TYPE_IKEV2_IPSEC_USER_PASS if the VPN not exist 531 if (!mExists) { 532 mProfile.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; 533 } 534 535 // Remove all types which are legacy types from the typesList 536 if (!VpnProfile.isLegacyType(mProfile.type)) { 537 for (int i = mAllowedTypes.size() - 1; i >= 0; i--) { 538 // This must be removed from back to front in order to ensure index consistency 539 if (VpnProfile.isLegacyType(i)) { 540 mAllowedTypes.remove(i); 541 } 542 } 543 544 types = mAllowedTypes.toArray(new String[0]); 545 } 546 final ArrayAdapter<String> adapter = new ArrayAdapter<String>( 547 getContext(), android.R.layout.simple_spinner_item, types); 548 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 549 typeSpinner.setAdapter(adapter); 550 } 551 loadCertificates(Spinner spinner, Collection<String> choices, int firstId, String selected)552 private void loadCertificates(Spinner spinner, Collection<String> choices, int firstId, 553 String selected) { 554 Context context = getContext(); 555 String first = (firstId == 0) ? "" : context.getString(firstId); 556 String[] myChoices; 557 558 if (choices == null || choices.size() == 0) { 559 myChoices = new String[] {first}; 560 } else { 561 myChoices = new String[choices.size() + 1]; 562 myChoices[0] = first; 563 int i = 1; 564 for (String c : choices) { 565 myChoices[i++] = c; 566 } 567 } 568 569 ArrayAdapter<String> adapter = new ArrayAdapter<String>( 570 context, android.R.layout.simple_spinner_item, myChoices); 571 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 572 spinner.setAdapter(adapter); 573 574 for (int i = 1; i < myChoices.length; ++i) { 575 if (myChoices[i].equals(selected)) { 576 spinner.setSelection(i); 577 break; 578 } 579 } 580 } 581 setUsernamePasswordVisibility(int type)582 private void setUsernamePasswordVisibility(int type) { 583 mView.findViewById(R.id.userpass).setVisibility( 584 requiresUsernamePassword(type) ? View.VISIBLE : View.GONE); 585 } 586 requiresUsernamePassword(int type)587 private boolean requiresUsernamePassword(int type) { 588 switch (type) { 589 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through 590 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: 591 return false; 592 default: 593 return true; 594 } 595 } 596 isEditing()597 boolean isEditing() { 598 return mEditing; 599 } 600 hasProxy()601 boolean hasProxy() { 602 return mProxySettings.getSelectedItemPosition() == VpnProfile.PROXY_MANUAL; 603 } 604 getProfile()605 VpnProfile getProfile() { 606 // First, save common fields. 607 VpnProfile profile = new VpnProfile(mProfile.key); 608 profile.name = mName.getText().toString(); 609 profile.type = getSelectedVpnType(); 610 profile.server = mServer.getText().toString().trim(); 611 profile.username = mUsername.getText().toString(); 612 profile.password = mPassword.getText().toString(); 613 614 // Save fields based on VPN type. 615 if (isLegacyType(profile.type)) { 616 // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes 617 profile.searchDomains = mSearchDomains.getText().toString().trim(); 618 profile.dnsServers = mDnsServers.getText().toString().trim(); 619 profile.routes = mRoutes.getText().toString().trim(); 620 } else { 621 profile.ipsecIdentifier = mIpsecIdentifier.getText().toString(); 622 } 623 624 if (hasProxy()) { 625 String proxyHost = mProxyHost.getText().toString().trim(); 626 String proxyPort = mProxyPort.getText().toString().trim(); 627 // 0 is a last resort default, but the interface validates that the proxy port is 628 // present and non-zero. 629 int port = proxyPort.isEmpty() ? 0 : Integer.parseInt(proxyPort); 630 profile.proxy = ProxyInfo.buildDirectProxy(proxyHost, port); 631 } else { 632 profile.proxy = null; 633 } 634 // Then, save type-specific fields. 635 switch (profile.type) { 636 case VpnProfile.TYPE_PPTP: 637 profile.mppe = mMppe.isChecked(); 638 break; 639 640 case VpnProfile.TYPE_L2TP_IPSEC_PSK: 641 profile.l2tpSecret = mL2tpSecret.getText().toString(); 642 // fall through 643 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through 644 case VpnProfile.TYPE_IPSEC_XAUTH_PSK: 645 profile.ipsecIdentifier = mIpsecIdentifier.getText().toString(); 646 profile.ipsecSecret = mIpsecSecret.getText().toString(); 647 break; 648 649 case VpnProfile.TYPE_L2TP_IPSEC_RSA: 650 profile.l2tpSecret = mL2tpSecret.getText().toString(); 651 // fall through 652 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through 653 case VpnProfile.TYPE_IPSEC_XAUTH_RSA: 654 if (mIpsecUserCert.getSelectedItemPosition() != 0) { 655 profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem(); 656 } 657 // fall through 658 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through 659 case VpnProfile.TYPE_IPSEC_HYBRID_RSA: 660 if (mIpsecCaCert.getSelectedItemPosition() != 0) { 661 profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem(); 662 } 663 if (mIpsecServerCert.getSelectedItemPosition() != 0) { 664 profile.ipsecServerCert = (String) mIpsecServerCert.getSelectedItem(); 665 } 666 break; 667 } 668 669 final boolean hasLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); 670 profile.saveLogin = mSaveLogin.isChecked() || (mEditing && hasLogin); 671 return profile; 672 } 673 validateProxy()674 private boolean validateProxy() { 675 if (!hasProxy()) { 676 return true; 677 } 678 679 final String host = mProxyHost.getText().toString().trim(); 680 final String port = mProxyPort.getText().toString().trim(); 681 return ProxyUtils.validate(host, port, "") == ProxyUtils.PROXY_VALID; 682 } 683 getSelectedVpnType()684 private int getSelectedVpnType() { 685 return convertAllowedIndexToProfileType(mType.getSelectedItemPosition()); 686 } 687 convertAllowedIndexToProfileType(int allowedSelectedPosition)688 private int convertAllowedIndexToProfileType(int allowedSelectedPosition) { 689 if (mAllowedTypes != null && mTotalTypes != null) { 690 final String typeString = mAllowedTypes.get(allowedSelectedPosition); 691 final int profileType = mTotalTypes.indexOf(typeString); 692 return profileType; 693 } else { 694 Log.w(TAG, "Allowed or Total vpn types not initialized when converting protileType"); 695 return allowedSelectedPosition; 696 } 697 } 698 699 } 700