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