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 android.content.Context; 20 import android.content.DialogInterface; 21 import android.content.pm.PackageManager; 22 import android.net.ProxyInfo; 23 import android.os.Bundle; 24 import android.os.SystemProperties; 25 import android.text.Editable; 26 import android.text.TextWatcher; 27 import android.util.Log; 28 import android.view.View; 29 import android.view.WindowManager; 30 import android.widget.AdapterView; 31 import android.widget.ArrayAdapter; 32 import android.widget.CheckBox; 33 import android.widget.CompoundButton; 34 import android.widget.Spinner; 35 import android.widget.TextView; 36 37 import androidx.appcompat.app.AlertDialog; 38 39 import com.android.internal.net.VpnProfile; 40 import com.android.net.module.util.ProxyUtils; 41 import com.android.settings.R; 42 import com.android.settings.utils.AndroidKeystoreAliasLoader; 43 import com.android.settings.wifi.utils.TextInputGroup; 44 45 import java.util.Collection; 46 import java.util.List; 47 48 /** 49 * Dialog showing information about a VPN configuration. The dialog 50 * can be launched to either edit or prompt for credentials to connect 51 * to a user-added VPN. 52 * 53 * {@see AppDialog} 54 */ 55 class ConfigDialog extends AlertDialog implements TextWatcher, 56 View.OnClickListener, AdapterView.OnItemSelectedListener, 57 CompoundButton.OnCheckedChangeListener { 58 private static final String TAG = "ConfigDialog"; 59 // Vpn profile constants to match with R.array.vpn_types. 60 private static final List<Integer> VPN_TYPES = List.of( 61 VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS, 62 VpnProfile.TYPE_IKEV2_IPSEC_PSK, 63 VpnProfile.TYPE_IKEV2_IPSEC_RSA 64 ); 65 66 private final DialogInterface.OnClickListener mListener; 67 private final VpnProfile mProfile; 68 69 private boolean mEditing; 70 private boolean mExists; 71 72 private View mView; 73 74 private TextInputGroup mNameInput; 75 private Spinner mType; 76 private TextInputGroup mServerInput; 77 private TextInputGroup mUsernameInput; 78 private TextInputGroup mPasswordInput; 79 private Spinner mProxySettings; 80 private TextView mProxyHost; 81 private TextView mProxyPort; 82 private TextInputGroup mIpsecIdentifierInput; 83 private TextInputGroup mIpsecSecretInput; 84 private Spinner mIpsecUserCert; 85 private Spinner mIpsecCaCert; 86 private Spinner mIpsecServerCert; 87 private CheckBox mSaveLogin; 88 private CheckBox mShowOptions; 89 private CheckBox mAlwaysOnVpn; 90 private TextView mAlwaysOnInvalidReason; 91 ConfigDialog(Context context, DialogInterface.OnClickListener listener, VpnProfile profile, boolean editing, boolean exists)92 ConfigDialog(Context context, DialogInterface.OnClickListener listener, 93 VpnProfile profile, boolean editing, boolean exists) { 94 super(context); 95 96 mListener = listener; 97 mProfile = profile; 98 mEditing = editing; 99 mExists = exists; 100 } 101 102 @Override onCreate(Bundle savedState)103 protected void onCreate(Bundle savedState) { 104 mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null); 105 setView(mView); 106 107 Context context = getContext(); 108 109 // First, find out all the fields. 110 mNameInput = new TextInputGroup(mView, R.id.name_layout, R.id.name, 111 R.string.vpn_field_required); 112 mType = (Spinner) mView.findViewById(R.id.type); 113 mServerInput = new TextInputGroup(mView, R.id.server_layout, R.id.server, 114 R.string.vpn_field_required); 115 mUsernameInput = new TextInputGroup(mView, R.id.username_layout, R.id.username, 116 R.string.vpn_field_required); 117 mPasswordInput = new TextInputGroup(mView, R.id.password_layout, R.id.password, 118 R.string.vpn_field_required); 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 mIpsecIdentifierInput = new TextInputGroup(mView, R.id.ipsec_identifier_layout, 123 R.id.ipsec_identifier, R.string.vpn_field_required); 124 mIpsecSecretInput = new TextInputGroup(mView, R.id.ipsec_secret_layout, R.id.ipsec_secret, 125 R.string.vpn_field_required); 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 mNameInput.setText(mProfile.name); 136 setTypesByFeature(mType); 137 mType.setSelection(convertVpnProfileConstantToTypeIndex(mProfile.type)); 138 mServerInput.setText(mProfile.server); 139 if (mProfile.saveLogin) { 140 mUsernameInput.setText(mProfile.username); 141 mPasswordInput.setText(mProfile.password); 142 } 143 if (mProfile.proxy != null) { 144 mProxyHost.setText(mProfile.proxy.getHost()); 145 int port = mProfile.proxy.getPort(); 146 mProxyPort.setText(port == 0 ? "" : Integer.toString(port)); 147 } 148 mIpsecIdentifierInput.setText(mProfile.ipsecIdentifier); 149 mIpsecSecretInput.setText(mProfile.ipsecSecret); 150 final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = 151 new AndroidKeystoreAliasLoader(null); 152 loadCertificates(mIpsecUserCert, androidKeystoreAliasLoader.getKeyCertAliases(), 0, 153 mProfile.ipsecUserCert); 154 loadCertificates(mIpsecCaCert, androidKeystoreAliasLoader.getCaCertAliases(), 155 R.string.vpn_no_ca_cert, mProfile.ipsecCaCert); 156 loadCertificates(mIpsecServerCert, androidKeystoreAliasLoader.getKeyCertAliases(), 157 R.string.vpn_no_server_cert, mProfile.ipsecServerCert); 158 mSaveLogin.setChecked(mProfile.saveLogin); 159 mAlwaysOnVpn.setChecked(mProfile.key.equals(VpnUtils.getLockdownVpn())); 160 mPasswordInput.getEditText() 161 .setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium); 162 163 // Hide lockdown VPN on devices that require IMS authentication 164 if (SystemProperties.getBoolean("persist.radio.imsregrequired", false)) { 165 mAlwaysOnVpn.setVisibility(View.GONE); 166 } 167 168 // Third, add listeners to required fields. 169 mNameInput.addTextChangedListener(this); 170 mType.setOnItemSelectedListener(this); 171 mServerInput.addTextChangedListener(this); 172 mUsernameInput.addTextChangedListener(this); 173 mPasswordInput.addTextChangedListener(this); 174 mProxySettings.setOnItemSelectedListener(this); 175 mProxyHost.addTextChangedListener(this); 176 mProxyPort.addTextChangedListener(this); 177 mIpsecIdentifierInput.addTextChangedListener(this); 178 mIpsecSecretInput.addTextChangedListener(this); 179 mIpsecUserCert.setOnItemSelectedListener(this); 180 mShowOptions.setOnClickListener(this); 181 mAlwaysOnVpn.setOnCheckedChangeListener(this); 182 183 // Fourth, determine whether to do editing or connecting. 184 mEditing = mEditing || !validate(true /*editing*/); 185 186 if (mEditing) { 187 setTitle(R.string.vpn_edit); 188 189 // Show common fields. 190 mView.findViewById(R.id.editor).setVisibility(View.VISIBLE); 191 192 // Show type-specific fields. 193 changeType(mProfile.type); 194 195 // Hide 'save login' when we are editing. 196 mSaveLogin.setVisibility(View.GONE); 197 198 configureAdvancedOptionsVisibility(); 199 200 if (mExists) { 201 // Create a button to forget the profile if it has already been saved.. 202 setButton(DialogInterface.BUTTON_NEUTRAL, 203 context.getString(R.string.vpn_forget), mListener); 204 } 205 206 // Create a button to save the profile. 207 setButton(DialogInterface.BUTTON_POSITIVE, 208 context.getString(R.string.vpn_save), mListener); 209 } else { 210 setTitle(context.getString(R.string.vpn_connect_to, mProfile.name)); 211 212 setUsernamePasswordVisibility(mProfile.type); 213 mUsernameInput.setLabel(context.getString(R.string.vpn_username_required)); 214 mUsernameInput.setHelperText(context.getString(R.string.vpn_field_required)); 215 mPasswordInput.setLabel(context.getString(R.string.vpn_password_required)); 216 mPasswordInput.setHelperText(context.getString(R.string.vpn_field_required)); 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(VPN_TYPES.get(position)); 271 } else if (parent == mProxySettings) { 272 updateProxyFieldsVisibility(position); 273 } 274 updateUiControls(); 275 mNameInput.setError(""); 276 mServerInput.setError(""); 277 mIpsecIdentifierInput.setError(""); 278 mIpsecSecretInput.setError(""); 279 } 280 281 @Override onNothingSelected(AdapterView<?> parent)282 public void onNothingSelected(AdapterView<?> parent) { 283 } 284 285 @Override onCheckedChanged(CompoundButton compoundButton, boolean b)286 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 287 if (compoundButton == mAlwaysOnVpn) { 288 updateUiControls(); 289 } 290 } 291 isVpnAlwaysOn()292 public boolean isVpnAlwaysOn() { 293 return mAlwaysOnVpn.isChecked(); 294 } 295 296 /** 297 * Updates the UI according to the current configuration entered by the user. 298 * 299 * These include: 300 * "Always-on VPN" checkbox 301 * Reason for "Always-on VPN" being disabled, when necessary 302 * Proxy info if manually configured 303 * "Save account information" checkbox 304 * "Save" and "Connect" buttons 305 */ updateUiControls()306 private void updateUiControls() { 307 VpnProfile profile = getProfile(); 308 309 // Always-on VPN 310 if (profile.isValidLockdownProfile()) { 311 mAlwaysOnVpn.setEnabled(true); 312 mAlwaysOnInvalidReason.setVisibility(View.GONE); 313 } else { 314 mAlwaysOnVpn.setChecked(false); 315 mAlwaysOnVpn.setEnabled(false); 316 mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_other); 317 mAlwaysOnInvalidReason.setVisibility(View.VISIBLE); 318 } 319 320 // Show proxy fields if any proxy field is filled. 321 if (mProfile.proxy != null && (!mProfile.proxy.getHost().isEmpty() || 322 mProfile.proxy.getPort() != 0)) { 323 mProxySettings.setSelection(VpnProfile.PROXY_MANUAL); 324 updateProxyFieldsVisibility(VpnProfile.PROXY_MANUAL); 325 } 326 327 // Save account information 328 if (mAlwaysOnVpn.isChecked()) { 329 mSaveLogin.setChecked(true); 330 mSaveLogin.setEnabled(false); 331 } else { 332 mSaveLogin.setChecked(mProfile.saveLogin); 333 mSaveLogin.setEnabled(true); 334 } 335 336 // Save or Connect button 337 getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing)); 338 } 339 updateProxyFieldsVisibility(int position)340 private void updateProxyFieldsVisibility(int position) { 341 final int visible = position == VpnProfile.PROXY_MANUAL ? View.VISIBLE : View.GONE; 342 mView.findViewById(R.id.vpn_proxy_fields).setVisibility(visible); 343 } 344 isAdvancedOptionsEnabled()345 private boolean isAdvancedOptionsEnabled() { 346 return mProxyHost.getText().length() > 0 || mProxyPort.getText().length() > 0; 347 } 348 configureAdvancedOptionsVisibility()349 private void configureAdvancedOptionsVisibility() { 350 if (mShowOptions.isChecked() || isAdvancedOptionsEnabled()) { 351 mView.findViewById(R.id.options).setVisibility(View.VISIBLE); 352 mShowOptions.setVisibility(View.GONE); 353 // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes 354 } else { 355 mView.findViewById(R.id.options).setVisibility(View.GONE); 356 mShowOptions.setVisibility(View.VISIBLE); 357 } 358 } 359 changeType(int type)360 private void changeType(int type) { 361 // First, hide everything. 362 mView.findViewById(R.id.ipsec_psk).setVisibility(View.GONE); 363 mView.findViewById(R.id.ipsec_user).setVisibility(View.GONE); 364 mView.findViewById(R.id.ipsec_peer).setVisibility(View.GONE); 365 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.GONE); 366 367 setUsernamePasswordVisibility(type); 368 369 // Always enable identity for IKEv2/IPsec profiles. 370 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE); 371 372 // Then, unhide type-specific fields. 373 switch (type) { 374 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: 375 mView.findViewById(R.id.ipsec_psk).setVisibility(View.VISIBLE); 376 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE); 377 break; 378 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: 379 mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE); 380 // fall through 381 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: 382 mView.findViewById(R.id.ipsec_peer).setVisibility(View.VISIBLE); 383 break; 384 } 385 386 configureAdvancedOptionsVisibility(); 387 } 388 validate(boolean editing)389 private boolean validate(boolean editing) { 390 if (mAlwaysOnVpn.isChecked() && !getProfile().isValidLockdownProfile()) { 391 return false; 392 } 393 394 if (!validateProxy()) { 395 return false; 396 } 397 398 switch (getVpnType()) { 399 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: 400 return true; 401 402 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: 403 return true; 404 405 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: 406 return mIpsecUserCert.getSelectedItemPosition() != 0; 407 } 408 return false; 409 } 410 validate()411 public boolean validate() { 412 boolean isValidate = true; 413 int type = getVpnType(); 414 if (!mEditing && requiresUsernamePassword(type)) { 415 if (!mUsernameInput.validate()) isValidate = false; 416 if (!mPasswordInput.validate()) isValidate = false; 417 return isValidate; 418 } 419 420 if (!mNameInput.validate()) isValidate = false; 421 if (!mServerInput.validate()) isValidate = false; 422 if (!mIpsecIdentifierInput.validate()) isValidate = false; 423 if (type == VpnProfile.TYPE_IKEV2_IPSEC_PSK && !mIpsecSecretInput.validate()) { 424 isValidate = false; 425 } 426 if (!isValidate) Log.w(TAG, "Failed to validate VPN profile!"); 427 return isValidate; 428 } 429 getVpnType()430 private int getVpnType() { 431 return VPN_TYPES.get(mType.getSelectedItemPosition()); 432 } 433 setTypesByFeature(Spinner typeSpinner)434 private void setTypesByFeature(Spinner typeSpinner) { 435 String[] types = getContext().getResources().getStringArray(R.array.vpn_types); 436 if (types.length != VPN_TYPES.size()) { 437 Log.wtf(TAG, "VPN_TYPES array length does not match string array"); 438 } 439 // Although FEATURE_IPSEC_TUNNELS should always be present in android S and beyond, 440 // keep this check here just to be safe. 441 if (!getContext().getPackageManager().hasSystemFeature( 442 PackageManager.FEATURE_IPSEC_TUNNELS)) { 443 Log.wtf(TAG, "FEATURE_IPSEC_TUNNELS missing from system"); 444 } 445 // If the vpn is new or is not already a legacy type, 446 // don't allow the user to set the type to a legacy option. 447 448 // Set the mProfile.type to TYPE_IKEV2_IPSEC_USER_PASS if the VPN not exist 449 if (!mExists) { 450 mProfile.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; 451 } 452 453 final ArrayAdapter<String> adapter = new ArrayAdapter<String>( 454 getContext(), android.R.layout.simple_spinner_item, types); 455 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 456 typeSpinner.setAdapter(adapter); 457 } 458 loadCertificates(Spinner spinner, Collection<String> choices, int firstId, String selected)459 private void loadCertificates(Spinner spinner, Collection<String> choices, int firstId, 460 String selected) { 461 Context context = getContext(); 462 String first = (firstId == 0) ? "" : context.getString(firstId); 463 String[] myChoices; 464 465 if (choices == null || choices.size() == 0) { 466 myChoices = new String[] {first}; 467 } else { 468 myChoices = new String[choices.size() + 1]; 469 myChoices[0] = first; 470 int i = 1; 471 for (String c : choices) { 472 myChoices[i++] = c; 473 } 474 } 475 476 ArrayAdapter<String> adapter = new ArrayAdapter<String>( 477 context, android.R.layout.simple_spinner_item, myChoices); 478 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 479 spinner.setAdapter(adapter); 480 481 for (int i = 1; i < myChoices.length; ++i) { 482 if (myChoices[i].equals(selected)) { 483 spinner.setSelection(i); 484 break; 485 } 486 } 487 } 488 setUsernamePasswordVisibility(int type)489 private void setUsernamePasswordVisibility(int type) { 490 mView.findViewById(R.id.userpass).setVisibility( 491 requiresUsernamePassword(type) ? View.VISIBLE : View.GONE); 492 } 493 requiresUsernamePassword(int type)494 private boolean requiresUsernamePassword(int type) { 495 switch (type) { 496 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through 497 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: 498 return false; 499 default: 500 return true; 501 } 502 } 503 isEditing()504 boolean isEditing() { 505 return mEditing; 506 } 507 hasProxy()508 boolean hasProxy() { 509 return mProxySettings.getSelectedItemPosition() == VpnProfile.PROXY_MANUAL; 510 } 511 getProfile()512 VpnProfile getProfile() { 513 // First, save common fields. 514 VpnProfile profile = new VpnProfile(mProfile.key); 515 profile.name = mNameInput.getText(); 516 profile.type = getVpnType(); 517 profile.server = mServerInput.getText().trim(); 518 profile.username = mUsernameInput.getText(); 519 profile.password = mPasswordInput.getText(); 520 521 // Save fields based on VPN type. 522 profile.ipsecIdentifier = mIpsecIdentifierInput.getText(); 523 524 if (hasProxy()) { 525 String proxyHost = mProxyHost.getText().toString().trim(); 526 String proxyPort = mProxyPort.getText().toString().trim(); 527 // 0 is a last resort default, but the interface validates that the proxy port is 528 // present and non-zero. 529 int port = 0; 530 if (!proxyPort.isEmpty()) { 531 try { 532 port = Integer.parseInt(proxyPort); 533 } catch (NumberFormatException e) { 534 Log.e(TAG, "Could not parse proxy port integer ", e); 535 } 536 } 537 profile.proxy = ProxyInfo.buildDirectProxy(proxyHost, port); 538 } else { 539 profile.proxy = null; 540 } 541 // Then, save type-specific fields. 542 switch (profile.type) { 543 case VpnProfile.TYPE_IKEV2_IPSEC_PSK: 544 profile.ipsecSecret = mIpsecSecretInput.getText(); 545 break; 546 547 case VpnProfile.TYPE_IKEV2_IPSEC_RSA: 548 if (mIpsecUserCert.getSelectedItemPosition() != 0) { 549 profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem(); 550 profile.ipsecSecret = profile.ipsecUserCert; 551 } 552 // fall through 553 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: 554 if (mIpsecCaCert.getSelectedItemPosition() != 0) { 555 profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem(); 556 } 557 if (mIpsecServerCert.getSelectedItemPosition() != 0) { 558 profile.ipsecServerCert = (String) mIpsecServerCert.getSelectedItem(); 559 } 560 break; 561 } 562 563 final boolean hasLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); 564 profile.saveLogin = mSaveLogin.isChecked() || (mEditing && hasLogin); 565 return profile; 566 } 567 validateProxy()568 private boolean validateProxy() { 569 if (!hasProxy()) { 570 return true; 571 } 572 573 final String host = mProxyHost.getText().toString().trim(); 574 final String port = mProxyPort.getText().toString().trim(); 575 return ProxyUtils.validate(host, port, "") == ProxyUtils.PROXY_VALID; 576 } 577 convertVpnProfileConstantToTypeIndex(int vpnType)578 private int convertVpnProfileConstantToTypeIndex(int vpnType) { 579 final int typeIndex = VPN_TYPES.indexOf(vpnType); 580 if (typeIndex == -1) { 581 // Existing legacy profile type 582 Log.wtf(TAG, "Invalid existing profile type"); 583 return 0; 584 } 585 return typeIndex; 586 } 587 } 588