1 /* 2 * Copyright (C) 2010 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.wifi; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.net.InetAddresses; 22 import android.net.IpConfiguration; 23 import android.net.IpConfiguration.IpAssignment; 24 import android.net.IpConfiguration.ProxySettings; 25 import android.net.LinkAddress; 26 import android.net.NetworkInfo.DetailedState; 27 import android.net.ProxyInfo; 28 import android.net.StaticIpConfiguration; 29 import android.net.Uri; 30 import android.net.wifi.WifiConfiguration; 31 import android.net.wifi.WifiEnterpriseConfig; 32 import android.net.wifi.WifiEnterpriseConfig.Eap; 33 import android.net.wifi.WifiEnterpriseConfig.Phase2; 34 import android.net.wifi.WifiInfo; 35 import android.net.wifi.WifiManager; 36 import android.os.IBinder; 37 import android.security.keystore.KeyProperties; 38 import android.telephony.SubscriptionInfo; 39 import android.telephony.SubscriptionManager; 40 import android.text.Editable; 41 import android.text.InputType; 42 import android.text.SpannableString; 43 import android.text.TextUtils; 44 import android.text.TextWatcher; 45 import android.util.Log; 46 import android.view.KeyEvent; 47 import android.view.View; 48 import android.view.View.AccessibilityDelegate; 49 import android.view.ViewGroup; 50 import android.view.accessibility.AccessibilityEvent; 51 import android.view.accessibility.AccessibilityNodeInfo; 52 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 53 import android.view.inputmethod.EditorInfo; 54 import android.view.inputmethod.InputMethodManager; 55 import android.widget.AdapterView; 56 import android.widget.ArrayAdapter; 57 import android.widget.Button; 58 import android.widget.CheckBox; 59 import android.widget.CompoundButton; 60 import android.widget.CompoundButton.OnCheckedChangeListener; 61 import android.widget.EditText; 62 import android.widget.ImageButton; 63 import android.widget.LinearLayout; 64 import android.widget.Spinner; 65 import android.widget.TextView; 66 67 import androidx.annotation.VisibleForTesting; 68 69 import com.android.net.module.util.NetUtils; 70 import com.android.net.module.util.ProxyUtils; 71 import com.android.settings.ProxySelector; 72 import com.android.settings.R; 73 import com.android.settings.network.SubscriptionUtil; 74 import com.android.settings.utils.AndroidKeystoreAliasLoader; 75 import com.android.settings.wifi.details2.WifiPrivacyPreferenceController; 76 import com.android.settings.wifi.dpp.WifiDppUtils; 77 import com.android.settingslib.Utils; 78 import com.android.settingslib.utils.ThreadUtils; 79 import com.android.settingslib.wifi.AccessPoint; 80 import com.android.wifi.flags.Flags; 81 import com.android.wifitrackerlib.WifiEntry; 82 83 import java.net.Inet4Address; 84 import java.net.InetAddress; 85 import java.util.ArrayList; 86 import java.util.Arrays; 87 import java.util.Collection; 88 import java.util.Collections; 89 import java.util.Iterator; 90 import java.util.List; 91 import java.util.stream.Collectors; 92 93 /** 94 * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to 95 * share the logic for controlling buttons, text fields, etc. 96 * 97 * Migrating from Wi-Fi SettingsLib to to WifiTrackerLib, this object will be removed in the near 98 * future, please develop in {@link WifiConfigController2}. 99 */ 100 public class WifiConfigController implements TextWatcher, 101 AdapterView.OnItemSelectedListener, OnCheckedChangeListener, 102 TextView.OnEditorActionListener, View.OnKeyListener { 103 private static final String TAG = "WifiConfigController"; 104 105 private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; 106 107 private final WifiConfigUiBase mConfigUi; 108 private final View mView; 109 private final AccessPoint mAccessPoint; 110 111 /* This value comes from "wifi_ip_settings" resource array */ 112 private static final int DHCP = 0; 113 private static final int STATIC_IP = 1; 114 115 /* Constants used for referring to the hidden state of a network. */ 116 public static final int HIDDEN_NETWORK = 1; 117 public static final int NOT_HIDDEN_NETWORK = 0; 118 119 /* These values come from "wifi_proxy_settings" resource array */ 120 public static final int PROXY_NONE = 0; 121 public static final int PROXY_STATIC = 1; 122 public static final int PROXY_PAC = 2; 123 124 /* These values come from "wifi_eap_method" resource array */ 125 public static final int WIFI_EAP_METHOD_PEAP = 0; 126 public static final int WIFI_EAP_METHOD_TLS = 1; 127 public static final int WIFI_EAP_METHOD_TTLS = 2; 128 public static final int WIFI_EAP_METHOD_PWD = 3; 129 public static final int WIFI_EAP_METHOD_SIM = 4; 130 public static final int WIFI_EAP_METHOD_AKA = 5; 131 public static final int WIFI_EAP_METHOD_AKA_PRIME = 6; 132 133 /* These values come from "wifi_peap_phase2_entries" resource array */ 134 public static final int WIFI_PEAP_PHASE2_MSCHAPV2 = 0; 135 public static final int WIFI_PEAP_PHASE2_GTC = 1; 136 public static final int WIFI_PEAP_PHASE2_SIM = 2; 137 public static final int WIFI_PEAP_PHASE2_AKA = 3; 138 public static final int WIFI_PEAP_PHASE2_AKA_PRIME = 4; 139 140 /* These values come from "wifi_ttls_phase2_entries" resource array */ 141 public static final int WIFI_TTLS_PHASE2_PAP = 0; 142 public static final int WIFI_TTLS_PHASE2_MSCHAP = 1; 143 public static final int WIFI_TTLS_PHASE2_MSCHAPV2 = 2; 144 public static final int WIFI_TTLS_PHASE2_GTC = 3; 145 146 private static final String UNDESIRED_CERTIFICATE_MACRANDSECRET = "MacRandSecret"; 147 private static final String UNDESIRED_CERTIFICATE_MACRANDSAPSECRET = "MacRandSapSecret"; 148 @VisibleForTesting 149 static final String[] UNDESIRED_CERTIFICATES = { 150 UNDESIRED_CERTIFICATE_MACRANDSECRET, 151 UNDESIRED_CERTIFICATE_MACRANDSAPSECRET 152 }; 153 154 // Should be the same index value as wifi_privacy_entries in arrays.xml 155 @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC = 0; 156 @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_DEVICE_MAC = 1; 157 158 // Should be the same index value as wifi_dhcp_entries in arrays.xml 159 @VisibleForTesting static final int DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE = 0; 160 @VisibleForTesting static final int DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_DISABLE = 1; 161 162 /* Phase2 methods supported by PEAP are limited */ 163 private ArrayAdapter<CharSequence> mPhase2PeapAdapter; 164 /* Phase2 methods supported by TTLS are limited */ 165 private ArrayAdapter<CharSequence> mPhase2TtlsAdapter; 166 167 // e.g. AccessPoint.SECURITY_NONE 168 @VisibleForTesting 169 int mAccessPointSecurity; 170 private TextView mPasswordView; 171 private ImageButton mSsidScanButton; 172 173 private String mUnspecifiedCertString; 174 private String mMultipleCertSetString; 175 private String mUseSystemCertsString; 176 private String mDoNotProvideEapUserCertString; 177 178 private Spinner mSecuritySpinner; 179 @VisibleForTesting Spinner mEapMethodSpinner; 180 @VisibleForTesting Spinner mEapSimSpinner; // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME. 181 private Spinner mEapCaCertSpinner; 182 private Spinner mEapOcspSpinner; 183 private TextView mEapDomainView; 184 private Spinner mPhase2Spinner; 185 // Associated with mPhase2Spinner, one of mPhase2TtlsAdapter or mPhase2PeapAdapter 186 private ArrayAdapter<CharSequence> mPhase2Adapter; 187 private Spinner mEapUserCertSpinner; 188 private TextView mEapIdentityView; 189 private TextView mEapAnonymousView; 190 191 private Spinner mIpSettingsSpinner; 192 private TextView mIpAddressView; 193 private TextView mGatewayView; 194 private TextView mNetworkPrefixLengthView; 195 private TextView mDns1View; 196 private TextView mDns2View; 197 198 private Spinner mProxySettingsSpinner; 199 private Spinner mMeteredSettingsSpinner; 200 private Spinner mHiddenSettingsSpinner; 201 private Spinner mPrivacySettingsSpinner; 202 private Spinner mDhcpSettingsSpinner; 203 private TextView mHiddenWarningView; 204 private TextView mProxyHostView; 205 private TextView mProxyPortView; 206 private TextView mProxyExclusionListView; 207 private TextView mProxyPacView; 208 private CheckBox mSharedCheckBox; 209 210 private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED; 211 private ProxySettings mProxySettings = ProxySettings.UNASSIGNED; 212 private ProxyInfo mHttpProxy = null; 213 private StaticIpConfiguration mStaticIpConfiguration = null; 214 private boolean mRequestFocus = true; 215 216 private String[] mLevels; 217 private int mMode; 218 private TextView mSsidView; 219 220 private Context mContext; 221 222 @VisibleForTesting 223 Integer mSecurityInPosition[]; 224 225 private final WifiManager mWifiManager; 226 227 private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>(); 228 WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode)229 public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, 230 int mode) { 231 this (parent, view, accessPoint, mode, true /* requestFocus */); 232 } 233 WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode, boolean requestFocus)234 public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, 235 int mode, boolean requestFocus) { 236 mConfigUi = parent; 237 238 mView = view; 239 mAccessPoint = accessPoint; 240 mContext = mConfigUi.getContext(); 241 mRequestFocus = requestFocus; 242 243 // Init Wi-Fi manager 244 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 245 initWifiConfigController(accessPoint, mode); 246 } 247 248 @VisibleForTesting WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode, WifiManager wifiManager)249 public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, 250 int mode, WifiManager wifiManager) { 251 mConfigUi = parent; 252 253 mView = view; 254 mAccessPoint = accessPoint; 255 mContext = mConfigUi.getContext(); 256 mWifiManager = wifiManager; 257 initWifiConfigController(accessPoint, mode); 258 } 259 initWifiConfigController(AccessPoint accessPoint, int mode)260 private void initWifiConfigController(AccessPoint accessPoint, int mode) { 261 262 mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE : 263 accessPoint.getSecurity(); 264 mMode = mode; 265 266 final Resources res = mContext.getResources(); 267 268 mLevels = res.getStringArray(R.array.wifi_signal); 269 if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean( 270 com.android.internal.R.bool.config_eap_sim_based_auth_supported)) { 271 mPhase2PeapAdapter = getSpinnerAdapter(R.array.wifi_peap_phase2_entries); 272 } else { 273 mPhase2PeapAdapter = getSpinnerAdapterWithEapMethodsTts( 274 R.array.wifi_peap_phase2_entries_with_sim_auth); 275 } 276 277 mPhase2TtlsAdapter = getSpinnerAdapter(R.array.wifi_ttls_phase2_entries); 278 279 mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified); 280 mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added); 281 mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs); 282 mDoNotProvideEapUserCertString = 283 mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); 284 285 if (Flags.androidVWifiApi() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) { 286 LinearLayout wepWarningLayout = 287 (LinearLayout) mView.findViewById(R.id.wep_warning_layout); 288 wepWarningLayout.setVisibility(View.VISIBLE); 289 } 290 291 mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button); 292 mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings); 293 mIpSettingsSpinner.setOnItemSelectedListener(this); 294 mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings); 295 mProxySettingsSpinner.setOnItemSelectedListener(this); 296 mSharedCheckBox = (CheckBox) mView.findViewById(R.id.shared); 297 mMeteredSettingsSpinner = mView.findViewById(R.id.metered_settings); 298 mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings); 299 mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings); 300 mDhcpSettingsSpinner = mView.findViewById(R.id.dhcp_settings); 301 if (mWifiManager.isConnectedMacRandomizationSupported()) { 302 View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields); 303 privacySettingsLayout.setVisibility(View.VISIBLE); 304 } 305 mHiddenSettingsSpinner.setOnItemSelectedListener(this); 306 mHiddenWarningView = mView.findViewById(R.id.hidden_settings_warning); 307 mHiddenWarningView.setVisibility( 308 mHiddenSettingsSpinner.getSelectedItemPosition() == NOT_HIDDEN_NETWORK 309 ? View.GONE 310 : View.VISIBLE); 311 mSecurityInPosition = new Integer[AccessPoint.SECURITY_MAX_VAL]; 312 313 if (mAccessPoint == null) { // new network 314 configureSecuritySpinner(); 315 mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); 316 } else { 317 mConfigUi.setTitle(mAccessPoint.getTitle()); 318 319 ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); 320 321 boolean showAdvancedFields = false; 322 if (mAccessPoint.isSaved()) { 323 WifiConfiguration config = mAccessPoint.getConfig(); 324 mMeteredSettingsSpinner.setSelection(config.meteredOverride); 325 mHiddenSettingsSpinner.setSelection(config.hiddenSSID 326 ? HIDDEN_NETWORK 327 : NOT_HIDDEN_NETWORK); 328 329 mPrivacySettingsSpinner.setSelection( 330 config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT 331 ? PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC : PRIVACY_SPINNER_INDEX_DEVICE_MAC); 332 333 mDhcpSettingsSpinner.setSelection( 334 config.isSendDhcpHostnameEnabled() 335 ? DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE : 336 DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_DISABLE 337 ); 338 339 if (config.getIpConfiguration().getIpAssignment() == IpAssignment.STATIC) { 340 mIpSettingsSpinner.setSelection(STATIC_IP); 341 showAdvancedFields = true; 342 // Display IP address. 343 StaticIpConfiguration staticConfig = config.getIpConfiguration() 344 .getStaticIpConfiguration(); 345 if (staticConfig != null && staticConfig.getIpAddress() != null) { 346 addRow(group, R.string.wifi_ip_address, 347 staticConfig.getIpAddress().getAddress().getHostAddress()); 348 } 349 } else { 350 mIpSettingsSpinner.setSelection(DHCP); 351 } 352 353 mSharedCheckBox.setEnabled(config.shared); 354 if (!config.shared) { 355 showAdvancedFields = true; 356 } 357 358 ProxySettings proxySettings = config.getIpConfiguration().getProxySettings(); 359 if (proxySettings == ProxySettings.STATIC) { 360 mProxySettingsSpinner.setSelection(PROXY_STATIC); 361 showAdvancedFields = true; 362 } else if (proxySettings == ProxySettings.PAC) { 363 mProxySettingsSpinner.setSelection(PROXY_PAC); 364 showAdvancedFields = true; 365 } else { 366 mProxySettingsSpinner.setSelection(PROXY_NONE); 367 } 368 if (config != null && config.isPasspoint()) { 369 addRow(group, R.string.passpoint_label, 370 String.format(mContext.getString(R.string.passpoint_content), 371 config.providerFriendlyName)); 372 } 373 } 374 375 if ((!mAccessPoint.isSaved() && !mAccessPoint.isActive() 376 && !mAccessPoint.isPasspointConfig()) 377 || mMode != WifiConfigUiBase.MODE_VIEW) { 378 showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true); 379 showIpConfigFields(); 380 showProxyFields(); 381 final CheckBox advancedTogglebox = 382 (CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox); 383 if (!showAdvancedFields) { 384 // Need to show Advanced Option button. 385 mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); 386 advancedTogglebox.setOnCheckedChangeListener(this); 387 advancedTogglebox.setChecked(showAdvancedFields); 388 setAdvancedOptionAccessibilityString(); 389 } 390 mView.findViewById(R.id.wifi_advanced_fields) 391 .setVisibility(showAdvancedFields ? View.VISIBLE : View.GONE); 392 } 393 394 if (mMode == WifiConfigUiBase.MODE_MODIFY) { 395 mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); 396 } else if (mMode == WifiConfigUiBase.MODE_CONNECT) { 397 mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); 398 } else { 399 final DetailedState state = mAccessPoint.getDetailedState(); 400 final String signalLevel = getSignalString(); 401 402 if ((state == null || state == DetailedState.DISCONNECTED) && signalLevel != null) { 403 mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); 404 } else { 405 if (state != null) { 406 boolean isEphemeral = mAccessPoint.isEphemeral(); 407 WifiConfiguration config = mAccessPoint.getConfig(); 408 String providerFriendlyName = null; 409 if (config != null && config.isPasspoint()) { 410 providerFriendlyName = config.providerFriendlyName; 411 } 412 String suggestionOrSpecifierPackageName = null; 413 if (config != null 414 && (config.fromWifiNetworkSpecifier 415 || config.fromWifiNetworkSuggestion)) { 416 suggestionOrSpecifierPackageName = config.creatorName; 417 } 418 String summary = AccessPoint.getSummary( 419 mConfigUi.getContext(), /* ssid */ null, state, isEphemeral, 420 suggestionOrSpecifierPackageName); 421 addRow(group, R.string.wifi_status, summary); 422 } 423 424 if (signalLevel != null) { 425 addRow(group, R.string.wifi_signal, signalLevel); 426 } 427 428 WifiInfo info = mAccessPoint.getInfo(); 429 if (info != null && info.getTxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) { 430 addRow(group, R.string.tx_wifi_speed, String.format( 431 res.getString(R.string.tx_link_speed), info.getTxLinkSpeedMbps())); 432 } 433 434 if (info != null && info.getRxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) { 435 addRow(group, R.string.rx_wifi_speed, String.format( 436 res.getString(R.string.rx_link_speed), info.getRxLinkSpeedMbps())); 437 } 438 439 if (info != null && info.getFrequency() != -1) { 440 final int frequency = info.getFrequency(); 441 String band = null; 442 443 if (frequency >= AccessPoint.LOWER_FREQ_24GHZ 444 && frequency < AccessPoint.HIGHER_FREQ_24GHZ) { 445 band = res.getString(R.string.wifi_band_24ghz); 446 } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ 447 && frequency < AccessPoint.HIGHER_FREQ_5GHZ) { 448 band = res.getString(R.string.wifi_band_5ghz); 449 } else { 450 Log.e(TAG, "Unexpected frequency " + frequency); 451 } 452 if (band != null) { 453 addRow(group, R.string.wifi_frequency, band); 454 } 455 } 456 457 addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false)); 458 mView.findViewById(R.id.ip_fields).setVisibility(View.GONE); 459 } 460 if (mAccessPoint.isSaved() || mAccessPoint.isActive() 461 || mAccessPoint.isPasspointConfig()) { 462 mConfigUi.setForgetButton(res.getString(R.string.wifi_forget)); 463 } 464 } 465 466 mSsidScanButton.setVisibility(View.GONE); 467 } 468 mSharedCheckBox.setVisibility(View.GONE); 469 470 mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel)); 471 if (mConfigUi.getSubmitButton() != null) { 472 enableSubmitIfAppropriate(); 473 } 474 475 // After done view show and hide, request focus from parameter. 476 if (mRequestFocus) { 477 mView.findViewById(R.id.l_wifidialog).requestFocus(); 478 } 479 } 480 addRow(ViewGroup group, int name, String value)481 private void addRow(ViewGroup group, int name, String value) { 482 View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false); 483 ((TextView) row.findViewById(R.id.name)).setText(name); 484 ((TextView) row.findViewById(R.id.value)).setText(value); 485 group.addView(row); 486 } 487 488 @VisibleForTesting getSignalString()489 String getSignalString() { 490 if (!mAccessPoint.isReachable()) { 491 return null; 492 } 493 final int level = mAccessPoint.getLevel(); 494 495 return (level > -1 && level < mLevels.length) ? mLevels[level] : null; 496 } 497 hideForgetButton()498 void hideForgetButton() { 499 Button forget = mConfigUi.getForgetButton(); 500 if (forget == null) return; 501 502 forget.setVisibility(View.GONE); 503 } 504 hideSubmitButton()505 void hideSubmitButton() { 506 Button submit = mConfigUi.getSubmitButton(); 507 if (submit == null) return; 508 509 submit.setVisibility(View.GONE); 510 } 511 512 /* show submit button if password, ip and proxy settings are valid */ enableSubmitIfAppropriate()513 void enableSubmitIfAppropriate() { 514 Button submit = mConfigUi.getSubmitButton(); 515 if (submit == null) return; 516 517 submit.setEnabled(isSubmittable()); 518 } 519 isValidPsk(String password)520 boolean isValidPsk(String password) { 521 if (password.length() == 64 && password.matches("[0-9A-Fa-f]{64}")) { 522 return true; 523 } else if (password.length() >= 8 && password.length() <= 63) { 524 return true; 525 } 526 return false; 527 } 528 isValidSaePassword(String password)529 boolean isValidSaePassword(String password) { 530 if (password.length() >= 1 && password.length() <= 63) { 531 return true; 532 } 533 return false; 534 } 535 isSubmittable()536 boolean isSubmittable() { 537 boolean enabled = false; 538 boolean passwordInvalid = false; 539 if (mPasswordView != null 540 && ((mAccessPointSecurity == AccessPoint.SECURITY_WEP 541 && mPasswordView.length() == 0) 542 || (mAccessPointSecurity == AccessPoint.SECURITY_PSK 543 && !isValidPsk(mPasswordView.getText().toString())) 544 || (mAccessPointSecurity == AccessPoint.SECURITY_SAE 545 && !isValidSaePassword(mPasswordView.getText().toString())))) { 546 passwordInvalid = true; 547 } 548 if ((mSsidView != null && mSsidView.length() == 0) 549 // If Accesspoint is not saved, apply passwordInvalid check 550 || ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid 551 // If AccessPoint is saved (modifying network) and password is changed, apply 552 // Invalid password check 553 || mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid 554 && mPasswordView.length() > 0)) { 555 enabled = false; 556 } else { 557 enabled = ipAndProxyFieldsAreValid(); 558 } 559 if ((mAccessPointSecurity == AccessPoint.SECURITY_EAP 560 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE 561 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) 562 && mEapCaCertSpinner != null 563 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { 564 String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); 565 if (caCertSelection.equals(mUnspecifiedCertString)) { 566 // Disallow submit if the user has not selected a CA certificate for an EAP network 567 // configuration. 568 enabled = false; 569 } else if (mEapDomainView != null 570 && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE 571 && TextUtils.isEmpty(mEapDomainView.getText().toString())) { 572 // Disallow submit if the user chooses to use a certificate for EAP server 573 // validation, but does not provide a domain. 574 enabled = false; 575 } 576 } 577 if ((mAccessPointSecurity == AccessPoint.SECURITY_EAP 578 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE 579 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) 580 && mEapUserCertSpinner != null 581 && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE 582 && mEapUserCertSpinner.getSelectedItem().equals(mUnspecifiedCertString)) { 583 // Disallow submit if the user has not selected a user certificate for an EAP network 584 // configuration. 585 enabled = false; 586 } 587 return enabled; 588 } 589 showWarningMessagesIfAppropriate()590 void showWarningMessagesIfAppropriate() { 591 mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE); 592 mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE); 593 mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE); 594 595 if (mSsidView != null) { 596 final String ssid = mSsidView.getText().toString(); 597 if (WifiUtils.isSSIDTooLong(ssid)) { 598 mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.VISIBLE); 599 } 600 } 601 if (mEapCaCertSpinner != null 602 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { 603 if (mEapDomainView != null 604 && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE 605 && TextUtils.isEmpty(mEapDomainView.getText().toString())) { 606 // Display warning if user chooses to use a certificate without restricting the 607 // server domain that these certificates can be used to validate. 608 mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE); 609 } 610 } 611 612 if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B && 613 mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_TLS) { 614 String userCertSelection = (String) mEapUserCertSpinner.getSelectedItem(); 615 if (userCertSelection.equals(mUnspecifiedCertString)) { 616 mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.VISIBLE); 617 } 618 } 619 } 620 getConfig()621 public WifiConfiguration getConfig() { 622 if (mMode == WifiConfigUiBase.MODE_VIEW) { 623 return null; 624 } 625 626 WifiConfiguration config = new WifiConfiguration(); 627 628 if (mAccessPoint == null) { 629 config.SSID = AccessPoint.convertToQuotedString( 630 mSsidView.getText().toString()); 631 // If the user adds a network manually, assume that it is hidden. 632 config.hiddenSSID = mHiddenSettingsSpinner.getSelectedItemPosition() == HIDDEN_NETWORK; 633 } else if (!mAccessPoint.isSaved()) { 634 config.SSID = AccessPoint.convertToQuotedString( 635 mAccessPoint.getSsidStr()); 636 } else { 637 config.networkId = mAccessPoint.getConfig().networkId; 638 config.hiddenSSID = mAccessPoint.getConfig().hiddenSSID; 639 } 640 641 config.shared = mSharedCheckBox.isChecked(); 642 643 switch (mAccessPointSecurity) { 644 case AccessPoint.SECURITY_NONE: 645 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); 646 break; 647 648 case AccessPoint.SECURITY_WEP: 649 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); 650 if (mPasswordView.length() != 0) { 651 int length = mPasswordView.length(); 652 String password = mPasswordView.getText().toString(); 653 // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) 654 if ((length == 10 || length == 26 || length == 58) 655 && password.matches("[0-9A-Fa-f]*")) { 656 config.wepKeys[0] = password; 657 } else { 658 config.wepKeys[0] = '"' + password + '"'; 659 } 660 } 661 break; 662 663 case AccessPoint.SECURITY_PSK: 664 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 665 if (mPasswordView.length() != 0) { 666 String password = mPasswordView.getText().toString(); 667 if (password.matches("[0-9A-Fa-f]{64}")) { 668 config.preSharedKey = password; 669 } else { 670 config.preSharedKey = '"' + password + '"'; 671 } 672 } 673 break; 674 675 case AccessPoint.SECURITY_EAP: 676 case AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE: 677 case AccessPoint.SECURITY_EAP_SUITE_B: 678 if (mEapMethodSpinner == null || mPhase2Spinner == null) { 679 break; 680 } 681 if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) { 682 // allowedSuiteBCiphers will be set according to certificate type 683 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); 684 } else if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE) { 685 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 686 } else { 687 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); 688 } 689 config.enterpriseConfig = new WifiEnterpriseConfig(); 690 int eapMethod = mEapMethodSpinner.getSelectedItemPosition(); 691 int phase2Method = mPhase2Spinner.getSelectedItemPosition(); 692 config.enterpriseConfig.setEapMethod(eapMethod); 693 switch (eapMethod) { 694 case Eap.PEAP: 695 // PEAP supports limited phase2 values 696 // Map the index from the mPhase2PeapAdapter to the one used 697 // by the API which has the full list of PEAP methods. 698 switch(phase2Method) { 699 case WIFI_PEAP_PHASE2_MSCHAPV2: 700 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); 701 break; 702 case WIFI_PEAP_PHASE2_GTC: 703 config.enterpriseConfig.setPhase2Method(Phase2.GTC); 704 break; 705 case WIFI_PEAP_PHASE2_SIM: 706 config.enterpriseConfig.setPhase2Method(Phase2.SIM); 707 break; 708 case WIFI_PEAP_PHASE2_AKA: 709 config.enterpriseConfig.setPhase2Method(Phase2.AKA); 710 break; 711 case WIFI_PEAP_PHASE2_AKA_PRIME: 712 config.enterpriseConfig.setPhase2Method(Phase2.AKA_PRIME); 713 break; 714 default: 715 Log.e(TAG, "Unknown phase2 method" + phase2Method); 716 break; 717 } 718 break; 719 case Eap.TTLS: 720 // The default index from mPhase2TtlsAdapter maps to the API 721 switch(phase2Method) { 722 case WIFI_TTLS_PHASE2_PAP: 723 config.enterpriseConfig.setPhase2Method(Phase2.PAP); 724 break; 725 case WIFI_TTLS_PHASE2_MSCHAP: 726 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAP); 727 break; 728 case WIFI_TTLS_PHASE2_MSCHAPV2: 729 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); 730 break; 731 case WIFI_TTLS_PHASE2_GTC: 732 config.enterpriseConfig.setPhase2Method(Phase2.GTC); 733 break; 734 default: 735 Log.e(TAG, "Unknown phase2 method" + phase2Method); 736 break; 737 } 738 break; 739 default: 740 break; 741 } 742 743 String caCert = (String) mEapCaCertSpinner.getSelectedItem(); 744 config.enterpriseConfig.setCaCertificateAliases(null); 745 config.enterpriseConfig.setCaPath(null); 746 config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString()); 747 if (caCert.equals(mUnspecifiedCertString)) { 748 // ca_cert already set to null, so do nothing. 749 } else if (caCert.equals(mUseSystemCertsString)) { 750 config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH); 751 } else if (caCert.equals(mMultipleCertSetString)) { 752 if (mAccessPoint != null) { 753 if (!mAccessPoint.isSaved()) { 754 Log.e(TAG, "Multiple certs can only be set " 755 + "when editing saved network"); 756 } 757 config.enterpriseConfig.setCaCertificateAliases( 758 mAccessPoint 759 .getConfig() 760 .enterpriseConfig 761 .getCaCertificateAliases()); 762 } 763 } else { 764 config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert}); 765 } 766 767 // ca_cert or ca_path should not both be non-null, since we only intend to let 768 // the use either their own certificate, or the system certificates, not both. 769 // The variable that is not used must explicitly be set to null, so that a 770 // previously-set value on a saved configuration will be erased on an update. 771 if (config.enterpriseConfig.getCaCertificateAliases() != null 772 && config.enterpriseConfig.getCaPath() != null) { 773 Log.e(TAG, "ca_cert (" 774 + Arrays.toString(config.enterpriseConfig.getCaCertificateAliases()) 775 + ") and ca_path (" 776 + config.enterpriseConfig.getCaPath() 777 + ") should not both be non-null"); 778 } 779 780 // Only set OCSP option if there is a valid CA certificate. 781 if (caCert.equals(mUnspecifiedCertString)) { 782 config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE); 783 } else { 784 config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition()); 785 } 786 787 String clientCert = (String) mEapUserCertSpinner.getSelectedItem(); 788 if (clientCert.equals(mUnspecifiedCertString) 789 || clientCert.equals(mDoNotProvideEapUserCertString)) { 790 // Note: |clientCert| should not be able to take the value |unspecifiedCert|, 791 // since we prevent such configurations from being saved. 792 clientCert = ""; 793 } 794 config.enterpriseConfig.setClientCertificateAlias(clientCert); 795 if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) { 796 config.enterpriseConfig.setIdentity(""); 797 config.enterpriseConfig.setAnonymousIdentity(""); 798 } else if (eapMethod == Eap.PWD) { 799 config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString()); 800 config.enterpriseConfig.setAnonymousIdentity(""); 801 } else { 802 config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString()); 803 config.enterpriseConfig.setAnonymousIdentity( 804 mEapAnonymousView.getText().toString()); 805 } 806 807 if (mPasswordView.isShown()) { 808 // For security reasons, a previous password is not displayed to user. 809 // Update only if it has been changed. 810 if (mPasswordView.length() > 0) { 811 config.enterpriseConfig.setPassword(mPasswordView.getText().toString()); 812 } 813 } else { 814 // clear password 815 config.enterpriseConfig.setPassword(mPasswordView.getText().toString()); 816 } 817 break; 818 case AccessPoint.SECURITY_SAE: 819 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); 820 if (mPasswordView.length() != 0) { 821 String password = mPasswordView.getText().toString(); 822 config.preSharedKey = '"' + password + '"'; 823 } 824 break; 825 826 case AccessPoint.SECURITY_OWE: 827 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); 828 break; 829 830 default: 831 return null; 832 } 833 834 if (config.enterpriseConfig.isAuthenticationSimBased() 835 && mActiveSubscriptionInfos.size() > 0) { 836 config.carrierId = mActiveSubscriptionInfos 837 .get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId(); 838 } 839 840 final IpConfiguration ipConfig = new IpConfiguration(); 841 ipConfig.setIpAssignment(mIpAssignment); 842 ipConfig.setProxySettings(mProxySettings); 843 ipConfig.setStaticIpConfiguration(mStaticIpConfiguration); 844 ipConfig.setHttpProxy(mHttpProxy); 845 config.setIpConfiguration(ipConfig); 846 if (mMeteredSettingsSpinner != null) { 847 config.meteredOverride = mMeteredSettingsSpinner.getSelectedItemPosition(); 848 } 849 850 if (mPrivacySettingsSpinner != null) { 851 config.macRandomizationSetting = mPrivacySettingsSpinner.getSelectedItemPosition() 852 == PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC 853 ? WifiConfiguration.RANDOMIZATION_PERSISTENT 854 : WifiConfiguration.RANDOMIZATION_NONE; 855 } 856 857 if (mDhcpSettingsSpinner != null) { 858 config.setSendDhcpHostnameEnabled(WifiPrivacyPreferenceController.Companion 859 .translatePrefValueToSendDhcpHostnameEnabled(mDhcpSettingsSpinner 860 .getSelectedItemPosition())); 861 } 862 863 return config; 864 } 865 ipAndProxyFieldsAreValid()866 private boolean ipAndProxyFieldsAreValid() { 867 mIpAssignment = 868 (mIpSettingsSpinner != null 869 && mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) 870 ? IpAssignment.STATIC 871 : IpAssignment.DHCP; 872 873 if (mIpAssignment == IpAssignment.STATIC) { 874 mStaticIpConfiguration = new StaticIpConfiguration(); 875 int result = validateIpConfigFields(mStaticIpConfiguration); 876 if (result != 0) { 877 return false; 878 } 879 } 880 881 final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition(); 882 mProxySettings = ProxySettings.NONE; 883 mHttpProxy = null; 884 if (selectedPosition == PROXY_STATIC && mProxyHostView != null) { 885 mProxySettings = ProxySettings.STATIC; 886 String host = mProxyHostView.getText().toString(); 887 String portStr = mProxyPortView.getText().toString(); 888 String exclusionList = mProxyExclusionListView.getText().toString(); 889 int port = 0; 890 int result = 0; 891 try { 892 port = Integer.parseInt(portStr); 893 result = ProxySelector.validate(host, portStr, exclusionList); 894 } catch (NumberFormatException e) { 895 result = R.string.proxy_error_invalid_port; 896 } 897 if (result == 0) { 898 mHttpProxy = ProxyInfo.buildDirectProxy( 899 host, port, Arrays.asList(exclusionList.split(","))); 900 } else { 901 return false; 902 } 903 } else if (selectedPosition == PROXY_PAC && mProxyPacView != null) { 904 mProxySettings = ProxySettings.PAC; 905 CharSequence uriSequence = mProxyPacView.getText(); 906 if (TextUtils.isEmpty(uriSequence)) { 907 return false; 908 } 909 Uri uri = Uri.parse(uriSequence.toString()); 910 if (uri == null) { 911 return false; 912 } 913 mHttpProxy = ProxyInfo.buildPacProxy(uri); 914 } 915 return true; 916 } 917 getIPv4Address(String text)918 private Inet4Address getIPv4Address(String text) { 919 try { 920 return (Inet4Address) InetAddresses.parseNumericAddress(text); 921 } catch (IllegalArgumentException | ClassCastException e) { 922 return null; 923 } 924 } 925 validateIpConfigFields(StaticIpConfiguration staticIpConfiguration)926 private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) { 927 if (mIpAddressView == null) return 0; 928 929 String ipAddr = mIpAddressView.getText().toString(); 930 if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address; 931 932 Inet4Address inetAddr = getIPv4Address(ipAddr); 933 if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) { 934 return R.string.wifi_ip_settings_invalid_ip_address; 935 } 936 // Copy all fields into the builder first and set desired value later with builder. 937 final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder() 938 .setDnsServers(staticIpConfiguration.getDnsServers()) 939 .setDomains(staticIpConfiguration.getDomains()) 940 .setGateway(staticIpConfiguration.getGateway()) 941 .setIpAddress(staticIpConfiguration.getIpAddress()); 942 try { 943 int networkPrefixLength = -1; 944 try { 945 networkPrefixLength = Integer.parseInt( 946 mNetworkPrefixLengthView.getText().toString()); 947 if (networkPrefixLength < 0 || networkPrefixLength > 32) { 948 return R.string.wifi_ip_settings_invalid_network_prefix_length; 949 } 950 staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength)); 951 } catch (NumberFormatException e) { 952 // Set the hint as default after user types in ip address 953 mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( 954 com.android.settingslib.R.string.wifi_network_prefix_length_hint)); 955 } catch (IllegalArgumentException e) { 956 return R.string.wifi_ip_settings_invalid_ip_address; 957 } 958 959 String gateway = mGatewayView.getText().toString(); 960 if (TextUtils.isEmpty(gateway)) { 961 try { 962 //Extract a default gateway from IP address 963 InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); 964 byte[] addr = netPart.getAddress(); 965 addr[addr.length - 1] = 1; 966 mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); 967 } catch (RuntimeException ee) { 968 } catch (java.net.UnknownHostException u) { 969 } 970 } else { 971 InetAddress gatewayAddr = getIPv4Address(gateway); 972 if (gatewayAddr == null) { 973 return R.string.wifi_ip_settings_invalid_gateway; 974 } 975 if (gatewayAddr.isMulticastAddress()) { 976 return R.string.wifi_ip_settings_invalid_gateway; 977 } 978 staticIPBuilder.setGateway(gatewayAddr); 979 } 980 981 String dns = mDns1View.getText().toString(); 982 InetAddress dnsAddr = null; 983 final ArrayList<InetAddress> dnsServers = new ArrayList<>(); 984 985 if (TextUtils.isEmpty(dns)) { 986 //If everything else is valid, provide hint as a default option 987 mDns1View.setText(mConfigUi.getContext().getString( 988 com.android.settingslib.R.string.wifi_dns1_hint)); 989 } else { 990 dnsAddr = getIPv4Address(dns); 991 if (dnsAddr == null) { 992 return R.string.wifi_ip_settings_invalid_dns; 993 } 994 dnsServers.add(dnsAddr); 995 } 996 997 if (mDns2View.length() > 0) { 998 dns = mDns2View.getText().toString(); 999 dnsAddr = getIPv4Address(dns); 1000 if (dnsAddr == null) { 1001 return R.string.wifi_ip_settings_invalid_dns; 1002 } 1003 dnsServers.add(dnsAddr); 1004 } 1005 staticIPBuilder.setDnsServers(dnsServers); 1006 return 0; 1007 } finally { 1008 // Caller of this method may rely on staticIpConfiguration, so build the final result 1009 // at the end of the method. 1010 staticIpConfiguration = staticIPBuilder.build(); 1011 } 1012 } 1013 showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates)1014 private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) { 1015 if (mAccessPointSecurity == AccessPoint.SECURITY_NONE || 1016 mAccessPointSecurity == AccessPoint.SECURITY_OWE) { 1017 mView.findViewById(R.id.security_fields).setVisibility(View.GONE); 1018 return; 1019 } 1020 mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE); 1021 1022 if (mPasswordView == null) { 1023 mPasswordView = (TextView) mView.findViewById(R.id.password); 1024 mPasswordView.addTextChangedListener(this); 1025 mPasswordView.setOnEditorActionListener(this); 1026 mPasswordView.setOnKeyListener(this); 1027 ((CheckBox) mView.findViewById(R.id.show_password)) 1028 .setOnCheckedChangeListener(this); 1029 1030 if (mAccessPoint != null && mAccessPoint.isSaved()) { 1031 mPasswordView.setHint(R.string.wifi_unchanged); 1032 } 1033 } 1034 1035 if (mAccessPointSecurity != AccessPoint.SECURITY_EAP && 1036 mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) { 1037 mView.findViewById(R.id.eap).setVisibility(View.GONE); 1038 return; 1039 } 1040 mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); 1041 1042 // TODO (b/140541213): Maybe we can remove initiateEnterpriseNetworkUi by moving code block 1043 boolean initiateEnterpriseNetworkUi = false; 1044 if (mEapMethodSpinner == null) { 1045 initiateEnterpriseNetworkUi = true; 1046 mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method); 1047 mEapMethodSpinner.setOnItemSelectedListener(this); 1048 mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim); 1049 mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2); 1050 mPhase2Spinner.setOnItemSelectedListener(this); 1051 mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert); 1052 mEapCaCertSpinner.setOnItemSelectedListener(this); 1053 mEapOcspSpinner = (Spinner) mView.findViewById(R.id.ocsp); 1054 mEapDomainView = (TextView) mView.findViewById(R.id.domain); 1055 mEapDomainView.addTextChangedListener(this); 1056 mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert); 1057 mEapUserCertSpinner.setOnItemSelectedListener(this); 1058 mEapIdentityView = (TextView) mView.findViewById(R.id.identity); 1059 mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous); 1060 1061 setAccessibilityDelegateForSecuritySpinners(); 1062 } 1063 1064 if (refreshEapMethods) { 1065 ArrayAdapter<CharSequence> eapMethodSpinnerAdapter; 1066 if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) { 1067 eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.wifi_eap_method); 1068 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); 1069 // WAP3-Enterprise 192-bit only allows EAP method TLS 1070 mEapMethodSpinner.setSelection(Eap.TLS); 1071 mEapMethodSpinner.setEnabled(false); 1072 } else if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean( 1073 com.android.internal.R.bool.config_eap_sim_based_auth_supported)) { 1074 eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.eap_method_without_sim_auth); 1075 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); 1076 mEapMethodSpinner.setEnabled(true); 1077 } else { 1078 eapMethodSpinnerAdapter = getSpinnerAdapterWithEapMethodsTts(R.array.wifi_eap_method); 1079 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); 1080 mEapMethodSpinner.setEnabled(true); 1081 } 1082 } 1083 1084 if (refreshCertificates) { 1085 loadSims(); 1086 1087 final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = 1088 getAndroidKeystoreAliasLoader(); 1089 loadCertificates( 1090 mEapCaCertSpinner, 1091 androidKeystoreAliasLoader.getCaCertAliases(), 1092 null /* noCertificateString */, 1093 false /* showMultipleCerts */, 1094 true /* showUsePreinstalledCertOption */); 1095 loadCertificates( 1096 mEapUserCertSpinner, 1097 androidKeystoreAliasLoader.getKeyCertAliases(), 1098 mDoNotProvideEapUserCertString, 1099 false /* showMultipleCerts */, 1100 false /* showUsePreinstalledCertOption */); 1101 // To avoid the user connects to a non-secure network unexpectedly, 1102 // request using system trusted certificates by default 1103 // unless the user explicitly chooses "Do not validate" or other 1104 // CA certificates. 1105 setSelection(mEapCaCertSpinner, mUseSystemCertsString); 1106 } 1107 1108 // Modifying an existing network 1109 if (initiateEnterpriseNetworkUi && mAccessPoint != null && mAccessPoint.isSaved()) { 1110 final WifiConfiguration wifiConfig = mAccessPoint.getConfig(); 1111 final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig; 1112 final int eapMethod = enterpriseConfig.getEapMethod(); 1113 final int phase2Method = enterpriseConfig.getPhase2Method(); 1114 mEapMethodSpinner.setSelection(eapMethod); 1115 showEapFieldsByMethod(eapMethod); 1116 switch (eapMethod) { 1117 case Eap.PEAP: 1118 switch (phase2Method) { 1119 case Phase2.MSCHAPV2: 1120 mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2); 1121 break; 1122 case Phase2.GTC: 1123 mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC); 1124 break; 1125 case Phase2.SIM: 1126 mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_SIM); 1127 break; 1128 case Phase2.AKA: 1129 mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA); 1130 break; 1131 case Phase2.AKA_PRIME: 1132 mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA_PRIME); 1133 break; 1134 default: 1135 Log.e(TAG, "Invalid phase 2 method " + phase2Method); 1136 break; 1137 } 1138 break; 1139 case Eap.TTLS: 1140 switch (phase2Method) { 1141 case Phase2.PAP: 1142 mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_PAP); 1143 break; 1144 case Phase2.MSCHAP: 1145 mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAP); 1146 break; 1147 case Phase2.MSCHAPV2: 1148 mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAPV2); 1149 break; 1150 case Phase2.GTC: 1151 mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_GTC); 1152 break; 1153 default: 1154 Log.e(TAG, "Invalid phase 2 method " + phase2Method); 1155 break; 1156 } 1157 break; 1158 default: 1159 break; 1160 } 1161 1162 if (enterpriseConfig.isAuthenticationSimBased()) { 1163 for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) { 1164 if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) { 1165 mEapSimSpinner.setSelection(i); 1166 break; 1167 } 1168 } 1169 } 1170 1171 if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) { 1172 setSelection(mEapCaCertSpinner, mUseSystemCertsString); 1173 } else { 1174 String[] caCerts = enterpriseConfig.getCaCertificateAliases(); 1175 if (caCerts == null) { 1176 setSelection(mEapCaCertSpinner, mUnspecifiedCertString); 1177 } else if (caCerts.length == 1) { 1178 setSelection(mEapCaCertSpinner, caCerts[0]); 1179 } else { 1180 final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = 1181 getAndroidKeystoreAliasLoader(); 1182 1183 // Reload the cert spinner with an extra "multiple certificates added" item. 1184 loadCertificates( 1185 mEapCaCertSpinner, 1186 androidKeystoreAliasLoader.getCaCertAliases(), 1187 null /* noCertificateString */, 1188 true /* showMultipleCerts */, 1189 true /* showUsePreinstalledCertOption */); 1190 setSelection(mEapCaCertSpinner, mMultipleCertSetString); 1191 } 1192 } 1193 mEapOcspSpinner.setSelection(enterpriseConfig.getOcsp()); 1194 mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch()); 1195 String userCert = enterpriseConfig.getClientCertificateAlias(); 1196 if (TextUtils.isEmpty(userCert)) { 1197 setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString); 1198 } else { 1199 setSelection(mEapUserCertSpinner, userCert); 1200 } 1201 mEapIdentityView.setText(enterpriseConfig.getIdentity()); 1202 mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity()); 1203 } else { 1204 showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition()); 1205 } 1206 } 1207 setAccessibilityDelegateForSecuritySpinners()1208 private void setAccessibilityDelegateForSecuritySpinners() { 1209 final AccessibilityDelegate selectedEventBlocker = new AccessibilityDelegate() { 1210 @Override 1211 public void sendAccessibilityEvent(View host, int eventType) { 1212 if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) { 1213 // Ignore TYPE_VIEW_SELECTED or there will be multiple Spinner selected 1214 // information for WifiController#showSecurityFields. 1215 return; 1216 } 1217 super.sendAccessibilityEvent(host, eventType); 1218 } 1219 }; 1220 1221 mEapMethodSpinner.setAccessibilityDelegate(selectedEventBlocker); 1222 mPhase2Spinner.setAccessibilityDelegate(selectedEventBlocker); 1223 mEapCaCertSpinner.setAccessibilityDelegate(selectedEventBlocker); 1224 mEapOcspSpinner.setAccessibilityDelegate(selectedEventBlocker); 1225 mEapUserCertSpinner.setAccessibilityDelegate(selectedEventBlocker); 1226 } 1227 1228 /** 1229 * EAP-PWD valid fields include 1230 * identity 1231 * password 1232 * EAP-PEAP valid fields include 1233 * phase2: MSCHAPV2, GTC, SIM, AKA, AKA' 1234 * ca_cert 1235 * identity 1236 * anonymous_identity 1237 * password (not required for SIM, AKA, AKA') 1238 * EAP-TLS valid fields include 1239 * user_cert 1240 * ca_cert 1241 * domain 1242 * identity 1243 * EAP-TTLS valid fields include 1244 * phase2: PAP, MSCHAP, MSCHAPV2, GTC 1245 * ca_cert 1246 * identity 1247 * anonymous_identity 1248 * password 1249 */ showEapFieldsByMethod(int eapMethod)1250 private void showEapFieldsByMethod(int eapMethod) { 1251 // Common defaults 1252 mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE); 1253 mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE); 1254 mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE); 1255 1256 // Defaults for most of the EAP methods and over-riden by 1257 // by certain EAP methods 1258 mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE); 1259 mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE); 1260 mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE); 1261 mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE); 1262 mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE); 1263 1264 Context context = mConfigUi.getContext(); 1265 switch (eapMethod) { 1266 case WIFI_EAP_METHOD_PWD: 1267 setPhase2Invisible(); 1268 setCaCertInvisible(); 1269 setOcspInvisible(); 1270 setDomainInvisible(); 1271 setAnonymousIdentInvisible(); 1272 setUserCertInvisible(); 1273 mView.findViewById(R.id.l_sim).setVisibility(View.GONE); 1274 break; 1275 case WIFI_EAP_METHOD_TLS: 1276 mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE); 1277 setPhase2Invisible(); 1278 setAnonymousIdentInvisible(); 1279 setPasswordInvisible(); 1280 mView.findViewById(R.id.l_sim).setVisibility(View.GONE); 1281 break; 1282 case WIFI_EAP_METHOD_PEAP: 1283 // Reset adapter if needed 1284 if (mPhase2Adapter != mPhase2PeapAdapter) { 1285 mPhase2Adapter = mPhase2PeapAdapter; 1286 mPhase2Spinner.setAdapter(mPhase2Adapter); 1287 } 1288 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE); 1289 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); 1290 showPeapFields(); 1291 setUserCertInvisible(); 1292 break; 1293 case WIFI_EAP_METHOD_TTLS: 1294 // Reset adapter if needed 1295 if (mPhase2Adapter != mPhase2TtlsAdapter) { 1296 mPhase2Adapter = mPhase2TtlsAdapter; 1297 mPhase2Spinner.setAdapter(mPhase2Adapter); 1298 } 1299 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE); 1300 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); 1301 setUserCertInvisible(); 1302 mView.findViewById(R.id.l_sim).setVisibility(View.GONE); 1303 break; 1304 case WIFI_EAP_METHOD_SIM: 1305 case WIFI_EAP_METHOD_AKA: 1306 case WIFI_EAP_METHOD_AKA_PRIME: 1307 setPhase2Invisible(); 1308 setAnonymousIdentInvisible(); 1309 setCaCertInvisible(); 1310 setOcspInvisible(); 1311 setDomainInvisible(); 1312 setUserCertInvisible(); 1313 setPasswordInvisible(); 1314 setIdentityInvisible(); 1315 break; 1316 } 1317 1318 if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { 1319 String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); 1320 if (eapCertSelection.equals(mUnspecifiedCertString)) { 1321 // Domain suffix matching is not relevant if the user hasn't chosen a CA 1322 // certificate yet, or chooses not to validate the EAP server. 1323 setDomainInvisible(); 1324 // Ocsp is an additional validation step for a server certifidate. 1325 // This field is not relevant if the user hasn't chosen a valid 1326 // CA certificate yet. 1327 setOcspInvisible(); 1328 } 1329 } 1330 } 1331 showPeapFields()1332 private void showPeapFields() { 1333 int phase2Method = mPhase2Spinner.getSelectedItemPosition(); 1334 if (phase2Method == WIFI_PEAP_PHASE2_SIM || phase2Method == WIFI_PEAP_PHASE2_AKA 1335 || phase2Method == WIFI_PEAP_PHASE2_AKA_PRIME) { 1336 mEapIdentityView.setText(""); 1337 mView.findViewById(R.id.l_identity).setVisibility(View.GONE); 1338 setPasswordInvisible(); 1339 mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE); 1340 } else { 1341 mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE); 1342 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); 1343 mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE); 1344 mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE); 1345 mView.findViewById(R.id.l_sim).setVisibility(View.GONE); 1346 } 1347 } 1348 setIdentityInvisible()1349 private void setIdentityInvisible() { 1350 mView.findViewById(R.id.l_identity).setVisibility(View.GONE); 1351 } 1352 setPhase2Invisible()1353 private void setPhase2Invisible() { 1354 mView.findViewById(R.id.l_phase2).setVisibility(View.GONE); 1355 } 1356 setCaCertInvisible()1357 private void setCaCertInvisible() { 1358 mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); 1359 setSelection(mEapCaCertSpinner, mUnspecifiedCertString); 1360 } 1361 setOcspInvisible()1362 private void setOcspInvisible() { 1363 mView.findViewById(R.id.l_ocsp).setVisibility(View.GONE); 1364 mEapOcspSpinner.setSelection(WifiEnterpriseConfig.OCSP_NONE); 1365 } 1366 setDomainInvisible()1367 private void setDomainInvisible() { 1368 mView.findViewById(R.id.l_domain).setVisibility(View.GONE); 1369 mEapDomainView.setText(""); 1370 } 1371 setUserCertInvisible()1372 private void setUserCertInvisible() { 1373 mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE); 1374 setSelection(mEapUserCertSpinner, mUnspecifiedCertString); 1375 } 1376 setAnonymousIdentInvisible()1377 private void setAnonymousIdentInvisible() { 1378 mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE); 1379 mEapAnonymousView.setText(""); 1380 } 1381 setPasswordInvisible()1382 private void setPasswordInvisible() { 1383 mPasswordView.setText(""); 1384 mView.findViewById(R.id.password_layout).setVisibility(View.GONE); 1385 mView.findViewById(R.id.show_password_layout).setVisibility(View.GONE); 1386 } 1387 setEapMethodInvisible()1388 private void setEapMethodInvisible() { 1389 mView.findViewById(R.id.eap).setVisibility(View.GONE); 1390 } 1391 showIpConfigFields()1392 private void showIpConfigFields() { 1393 WifiConfiguration config = null; 1394 1395 mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE); 1396 1397 if (mAccessPoint != null && mAccessPoint.isSaved()) { 1398 config = mAccessPoint.getConfig(); 1399 } 1400 1401 if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) { 1402 mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE); 1403 if (mIpAddressView == null) { 1404 mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress); 1405 mIpAddressView.addTextChangedListener(this); 1406 mGatewayView = (TextView) mView.findViewById(R.id.gateway); 1407 mGatewayView.addTextChangedListener(this); 1408 mNetworkPrefixLengthView = (TextView) mView.findViewById( 1409 R.id.network_prefix_length); 1410 mNetworkPrefixLengthView.addTextChangedListener(this); 1411 mDns1View = (TextView) mView.findViewById(R.id.dns1); 1412 mDns1View.addTextChangedListener(this); 1413 mDns2View = (TextView) mView.findViewById(R.id.dns2); 1414 mDns2View.addTextChangedListener(this); 1415 } 1416 if (config != null) { 1417 StaticIpConfiguration staticConfig = config.getIpConfiguration() 1418 .getStaticIpConfiguration(); 1419 if (staticConfig != null) { 1420 if (staticConfig.getIpAddress() != null) { 1421 mIpAddressView.setText( 1422 staticConfig.getIpAddress().getAddress().getHostAddress()); 1423 mNetworkPrefixLengthView.setText(Integer.toString( 1424 staticConfig.getIpAddress().getPrefixLength())); 1425 } 1426 1427 if (staticConfig.getGateway() != null) { 1428 mGatewayView.setText(staticConfig.getGateway().getHostAddress()); 1429 } 1430 1431 Iterator<InetAddress> dnsIterator = staticConfig.getDnsServers().iterator(); 1432 if (dnsIterator.hasNext()) { 1433 mDns1View.setText(dnsIterator.next().getHostAddress()); 1434 } 1435 if (dnsIterator.hasNext()) { 1436 mDns2View.setText(dnsIterator.next().getHostAddress()); 1437 } 1438 } 1439 } 1440 } else { 1441 mView.findViewById(R.id.staticip).setVisibility(View.GONE); 1442 } 1443 } 1444 showProxyFields()1445 private void showProxyFields() { 1446 WifiConfiguration config = null; 1447 1448 mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE); 1449 1450 if (mAccessPoint != null && mAccessPoint.isSaved()) { 1451 config = mAccessPoint.getConfig(); 1452 } 1453 1454 if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) { 1455 setVisibility(R.id.proxy_warning_limited_support, View.VISIBLE); 1456 setVisibility(R.id.proxy_fields, View.VISIBLE); 1457 setVisibility(R.id.proxy_pac_field, View.GONE); 1458 if (mProxyHostView == null) { 1459 mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname); 1460 mProxyHostView.addTextChangedListener(this); 1461 mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port); 1462 mProxyPortView.addTextChangedListener(this); 1463 mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist); 1464 mProxyExclusionListView.addTextChangedListener(this); 1465 } 1466 if (config != null) { 1467 ProxyInfo proxyProperties = config.getHttpProxy(); 1468 if (proxyProperties != null) { 1469 mProxyHostView.setText(proxyProperties.getHost()); 1470 mProxyPortView.setText(Integer.toString(proxyProperties.getPort())); 1471 mProxyExclusionListView.setText( 1472 ProxyUtils.exclusionListAsString(proxyProperties.getExclusionList())); 1473 } 1474 } 1475 } else if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_PAC) { 1476 setVisibility(R.id.proxy_warning_limited_support, View.GONE); 1477 setVisibility(R.id.proxy_fields, View.GONE); 1478 setVisibility(R.id.proxy_pac_field, View.VISIBLE); 1479 1480 if (mProxyPacView == null) { 1481 mProxyPacView = (TextView) mView.findViewById(R.id.proxy_pac); 1482 mProxyPacView.addTextChangedListener(this); 1483 } 1484 if (config != null) { 1485 ProxyInfo proxyInfo = config.getHttpProxy(); 1486 if (proxyInfo != null) { 1487 mProxyPacView.setText(proxyInfo.getPacFileUrl().toString()); 1488 } 1489 } 1490 } else { 1491 setVisibility(R.id.proxy_warning_limited_support, View.GONE); 1492 setVisibility(R.id.proxy_fields, View.GONE); 1493 setVisibility(R.id.proxy_pac_field, View.GONE); 1494 } 1495 } 1496 setVisibility(int id, int visibility)1497 private void setVisibility(int id, int visibility) { 1498 final View v = mView.findViewById(id); 1499 if (v != null) { 1500 v.setVisibility(visibility); 1501 } 1502 } 1503 1504 @VisibleForTesting getAndroidKeystoreAliasLoader()1505 AndroidKeystoreAliasLoader getAndroidKeystoreAliasLoader() { 1506 return new AndroidKeystoreAliasLoader(KeyProperties.NAMESPACE_WIFI); 1507 } 1508 1509 @VisibleForTesting loadSims()1510 void loadSims() { 1511 List<SubscriptionInfo> activeSubscriptionInfos = mContext 1512 .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList(); 1513 if (activeSubscriptionInfos == null) { 1514 activeSubscriptionInfos = Collections.EMPTY_LIST; 1515 } 1516 mActiveSubscriptionInfos.clear(); 1517 1518 // De-duplicates active subscriptions and caches in mActiveSubscriptionInfos. 1519 for (SubscriptionInfo newInfo : activeSubscriptionInfos) { 1520 for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) { 1521 if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) { 1522 continue; 1523 } 1524 } 1525 mActiveSubscriptionInfos.add(newInfo); 1526 } 1527 1528 // Shows disabled 'No SIM' when there is no active subscription. 1529 if (mActiveSubscriptionInfos.size() == 0) { 1530 final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)}; 1531 mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim)); 1532 mEapSimSpinner.setSelection(0 /* position */); 1533 mEapSimSpinner.setEnabled(false); 1534 return; 1535 } 1536 1537 // Shows display name of each active subscription. 1538 final ArrayList<CharSequence> displayNames = new ArrayList<>(); 1539 for (SubscriptionInfo activeSubInfo : mActiveSubscriptionInfos) { 1540 displayNames.add( 1541 SubscriptionUtil.getUniqueSubscriptionDisplayName(activeSubInfo, mContext)); 1542 } 1543 mEapSimSpinner.setAdapter( 1544 getSpinnerAdapter(displayNames.toArray(new String[displayNames.size()]))); 1545 mEapSimSpinner.setSelection(0 /* position */); 1546 if (displayNames.size() == 1) { 1547 mEapSimSpinner.setEnabled(false); 1548 } 1549 } 1550 1551 @VisibleForTesting loadCertificates( Spinner spinner, Collection<String> choices, String noCertificateString, boolean showMultipleCerts, boolean showUsePreinstalledCertOption)1552 void loadCertificates( 1553 Spinner spinner, 1554 Collection<String> choices, 1555 String noCertificateString, 1556 boolean showMultipleCerts, 1557 boolean showUsePreinstalledCertOption) { 1558 final Context context = mConfigUi.getContext(); 1559 1560 ArrayList<String> certs = new ArrayList<String>(); 1561 certs.add(mUnspecifiedCertString); 1562 if (showMultipleCerts) { 1563 certs.add(mMultipleCertSetString); 1564 } 1565 if (showUsePreinstalledCertOption) { 1566 certs.add(mUseSystemCertsString); 1567 } 1568 1569 if (choices != null && choices.size() != 0) { 1570 certs.addAll(choices.stream() 1571 .filter(certificateName -> { 1572 for (String undesired : UNDESIRED_CERTIFICATES) { 1573 if (certificateName.startsWith(undesired)) { 1574 return false; 1575 } 1576 } 1577 return true; 1578 }).collect(Collectors.toList())); 1579 } 1580 1581 if (!TextUtils.isEmpty(noCertificateString) 1582 && mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) { 1583 certs.add(noCertificateString); 1584 } 1585 1586 // If there is only mUnspecifiedCertString and one item to select, only shows the item 1587 if (certs.size() == 2) { 1588 certs.remove(mUnspecifiedCertString); 1589 spinner.setEnabled(false); 1590 } else { 1591 spinner.setEnabled(true); 1592 } 1593 1594 final ArrayAdapter<CharSequence> adapter = getSpinnerAdapter( 1595 certs.toArray(new String[certs.size()])); 1596 spinner.setAdapter(adapter); 1597 } 1598 setSelection(Spinner spinner, String value)1599 private void setSelection(Spinner spinner, String value) { 1600 if (value != null) { 1601 @SuppressWarnings("unchecked") 1602 ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter(); 1603 for (int i = adapter.getCount() - 1; i >= 0; --i) { 1604 if (value.equals(adapter.getItem(i))) { 1605 spinner.setSelection(i); 1606 break; 1607 } 1608 } 1609 } 1610 } 1611 getMode()1612 public int getMode() { 1613 return mMode; 1614 } 1615 1616 @Override afterTextChanged(Editable s)1617 public void afterTextChanged(Editable s) { 1618 ThreadUtils.postOnMainThread(() -> { 1619 showWarningMessagesIfAppropriate(); 1620 enableSubmitIfAppropriate(); 1621 }); 1622 } 1623 1624 @Override beforeTextChanged(CharSequence s, int start, int count, int after)1625 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 1626 // work done in afterTextChanged 1627 } 1628 1629 @Override onTextChanged(CharSequence s, int start, int before, int count)1630 public void onTextChanged(CharSequence s, int start, int before, int count) { 1631 // work done in afterTextChanged 1632 } 1633 1634 @Override onEditorAction(TextView textView, int id, KeyEvent keyEvent)1635 public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 1636 if (textView == mPasswordView) { 1637 if (id == EditorInfo.IME_ACTION_DONE && isSubmittable()) { 1638 mConfigUi.dispatchSubmit(); 1639 return true; 1640 } 1641 } 1642 return false; 1643 } 1644 1645 @Override onKey(View view, int keyCode, KeyEvent keyEvent)1646 public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { 1647 if (view == mPasswordView) { 1648 if (keyCode == KeyEvent.KEYCODE_ENTER && isSubmittable()) { 1649 mConfigUi.dispatchSubmit(); 1650 return true; 1651 } 1652 } 1653 return false; 1654 } 1655 1656 @Override onCheckedChanged(CompoundButton view, boolean isChecked)1657 public void onCheckedChanged(CompoundButton view, boolean isChecked) { 1658 if (view.getId() == R.id.show_password) { 1659 int pos = mPasswordView.getSelectionEnd(); 1660 mPasswordView.setInputType(InputType.TYPE_CLASS_TEXT 1661 | (isChecked ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD 1662 : InputType.TYPE_TEXT_VARIATION_PASSWORD)); 1663 if (pos >= 0) { 1664 ((EditText) mPasswordView).setSelection(pos); 1665 } 1666 } else if (view.getId() == R.id.wifi_advanced_togglebox) { 1667 // Hide the SoftKeyboard temporary to let user can see most of the expanded items. 1668 hideSoftKeyboard(mView.getWindowToken()); 1669 view.setVisibility(View.GONE); 1670 mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE); 1671 } 1672 } 1673 1674 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)1675 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 1676 if (parent == mSecuritySpinner) { 1677 // Convert menu position to actual Wi-Fi security type 1678 mAccessPointSecurity = mSecurityInPosition[position]; 1679 showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true); 1680 1681 if (WifiDppUtils.isSupportEnrolleeQrCodeScanner(mContext, mAccessPointSecurity)) { 1682 mSsidScanButton.setVisibility(View.VISIBLE); 1683 } else { 1684 mSsidScanButton.setVisibility(View.GONE); 1685 } 1686 } else if (parent == mEapMethodSpinner) { 1687 showSecurityFields(/* refreshEapMethods */ false, /* refreshCertificates */ true); 1688 } else if (parent == mEapCaCertSpinner) { 1689 showSecurityFields(/* refreshEapMethods */ false, /* refreshCertificates */ false); 1690 } else if (parent == mPhase2Spinner 1691 && mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_PEAP) { 1692 showPeapFields(); 1693 } else if (parent == mProxySettingsSpinner) { 1694 showProxyFields(); 1695 } else if (parent == mHiddenSettingsSpinner) { 1696 mHiddenWarningView.setVisibility(position == NOT_HIDDEN_NETWORK 1697 ? View.GONE : View.VISIBLE); 1698 } else { 1699 showIpConfigFields(); 1700 } 1701 showWarningMessagesIfAppropriate(); 1702 enableSubmitIfAppropriate(); 1703 } 1704 1705 @Override onNothingSelected(AdapterView<?> parent)1706 public void onNothingSelected(AdapterView<?> parent) { 1707 // 1708 } 1709 1710 /** 1711 * Make the characters of the password visible if show_password is checked. 1712 */ updatePassword()1713 public void updatePassword() { 1714 TextView passwdView = (TextView) mView.findViewById(R.id.password); 1715 passwdView.setInputType(InputType.TYPE_CLASS_TEXT 1716 | (((CheckBox) mView.findViewById(R.id.show_password)).isChecked() 1717 ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD 1718 : InputType.TYPE_TEXT_VARIATION_PASSWORD)); 1719 } 1720 getAccessPoint()1721 public AccessPoint getAccessPoint() { 1722 return mAccessPoint; 1723 } 1724 configureSecuritySpinner()1725 private void configureSecuritySpinner() { 1726 mConfigUi.setTitle(R.string.wifi_add_network); 1727 1728 mSsidView = (TextView) mView.findViewById(R.id.ssid); 1729 mSsidView.addTextChangedListener(this); 1730 mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security)); 1731 mSecuritySpinner.setOnItemSelectedListener(this); 1732 1733 ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext, 1734 android.R.layout.simple_spinner_item, android.R.id.text1); 1735 spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 1736 mSecuritySpinner.setAdapter(spinnerAdapter); 1737 int idx = 0; 1738 1739 // Populate the Wi-Fi security spinner with the various supported key management types 1740 spinnerAdapter.add(mContext.getString(com.android.settingslib.R.string.wifi_security_none)); 1741 mSecurityInPosition[idx++] = AccessPoint.SECURITY_NONE; 1742 if (mWifiManager.isEnhancedOpenSupported()) { 1743 spinnerAdapter.add( 1744 mContext.getString(com.android.settingslib.R.string.wifi_security_owe)); 1745 mSecurityInPosition[idx++] = AccessPoint.SECURITY_OWE; 1746 } 1747 spinnerAdapter.add(mContext.getString(com.android.settingslib.R.string.wifi_security_wep)); 1748 mSecurityInPosition[idx++] = AccessPoint.SECURITY_WEP; 1749 spinnerAdapter.add( 1750 mContext.getString(com.android.settingslib.R.string.wifi_security_wpa_wpa2)); 1751 mSecurityInPosition[idx++] = AccessPoint.SECURITY_PSK; 1752 if (mWifiManager.isWpa3SaeSupported()) { 1753 spinnerAdapter.add( 1754 mContext.getString(com.android.settingslib.R.string.wifi_security_sae)); 1755 mSecurityInPosition[idx++] = AccessPoint.SECURITY_SAE; 1756 spinnerAdapter.add(mContext.getString( 1757 com.android.settingslib.R.string.wifi_security_eap_wpa_wpa2)); 1758 mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP; 1759 spinnerAdapter.add( 1760 mContext.getString(com.android.settingslib.R.string.wifi_security_eap_wpa3)); 1761 mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE; 1762 } else { 1763 spinnerAdapter.add( 1764 mContext.getString(com.android.settingslib.R.string.wifi_security_eap)); 1765 mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP; 1766 } 1767 if (mWifiManager.isWpa3SuiteBSupported()) { 1768 spinnerAdapter.add( 1769 mContext.getString(com.android.settingslib.R.string.wifi_security_eap_suiteb)); 1770 mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP_SUITE_B; 1771 } 1772 1773 spinnerAdapter.notifyDataSetChanged(); 1774 1775 mView.findViewById(R.id.type).setVisibility(View.VISIBLE); 1776 1777 showIpConfigFields(); 1778 showProxyFields(); 1779 mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); 1780 // Hidden option can be changed only when the user adds a network manually. 1781 mView.findViewById(R.id.hidden_settings_field).setVisibility(View.VISIBLE); 1782 ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox)) 1783 .setOnCheckedChangeListener(this); 1784 // Set correct accessibility strings. 1785 setAdvancedOptionAccessibilityString(); 1786 } 1787 1788 /** 1789 * For each target string in {@code targetStringArray} try to find if it appears in {@code 1790 * originalStringArray}, if found then use the corresponding string, which have the same index 1791 * of the target string in {@code replacementStringArray}, to replace it. And finally return the 1792 * whole new string array back to caller. 1793 */ 1794 @VisibleForTesting findAndReplaceTargetStrings(CharSequence originalStringArray[], CharSequence targetStringArray[], CharSequence replacementStringArray[])1795 CharSequence[] findAndReplaceTargetStrings(CharSequence originalStringArray[], 1796 CharSequence targetStringArray[], CharSequence replacementStringArray[]) { 1797 // The length of the targetStringArray and replacementStringArray should be the same, each 1798 // item in the targetStringArray should have a 1:1 mapping to replacementStringArray, so 1799 // just return the original string if the lengths are different. 1800 if (targetStringArray.length != replacementStringArray.length) { 1801 return originalStringArray; 1802 } 1803 1804 final CharSequence[] returnEntries = new CharSequence[originalStringArray.length]; 1805 for (int i = 0; i < originalStringArray.length; i++) { 1806 returnEntries[i] = originalStringArray[i]; 1807 for (int j = 0; j < targetStringArray.length; j++) { 1808 if (TextUtils.equals(originalStringArray[i], targetStringArray[j])) { 1809 returnEntries[i] = replacementStringArray[j]; 1810 } 1811 } 1812 } 1813 return returnEntries; 1814 } 1815 getSpinnerAdapter( int contentStringArrayResId)1816 private ArrayAdapter<CharSequence> getSpinnerAdapter( 1817 int contentStringArrayResId) { 1818 return getSpinnerAdapter( 1819 mContext.getResources().getStringArray(contentStringArrayResId)); 1820 } 1821 1822 @VisibleForTesting getSpinnerAdapter( String[] contentStringArray)1823 ArrayAdapter<CharSequence> getSpinnerAdapter( 1824 String[] contentStringArray) { 1825 ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext, 1826 android.R.layout.simple_spinner_item, contentStringArray); 1827 spinnerAdapter.setDropDownViewResource( 1828 android.R.layout.simple_spinner_dropdown_item); 1829 return spinnerAdapter; 1830 } 1831 1832 /** 1833 * This function is to span the TTS strings to each EAP method items in the 1834 * spinner to have detail TTS content for the TTS engine usage. 1835 */ getSpinnerAdapterWithEapMethodsTts( int contentStringArrayResId)1836 private ArrayAdapter<CharSequence> getSpinnerAdapterWithEapMethodsTts( 1837 int contentStringArrayResId) { 1838 final Resources res = mContext.getResources(); 1839 CharSequence[] sourceStrings = res.getStringArray( 1840 contentStringArrayResId); 1841 CharSequence[] targetStrings = res.getStringArray( 1842 R.array.wifi_eap_method_target_strings); 1843 CharSequence[] ttsStrings = res.getStringArray( 1844 R.array.wifi_eap_method_tts_strings); 1845 1846 // Replace the target strings with tts strings and save all in a new array. 1847 final CharSequence[] newTtsSourceStrings = findAndReplaceTargetStrings( 1848 sourceStrings, targetStrings, ttsStrings); 1849 1850 // Build new TtsSpan text arrays for TalkBack. 1851 final CharSequence[] accessibilityArray = createAccessibleEntries( 1852 sourceStrings, newTtsSourceStrings); 1853 1854 // Return a new ArrayAdapter with the new TalkBack array. 1855 ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>( 1856 mContext, android.R.layout.simple_spinner_item, accessibilityArray); 1857 spinnerAdapter.setDropDownViewResource( 1858 android.R.layout.simple_spinner_dropdown_item); 1859 return spinnerAdapter; 1860 } 1861 createAccessibleEntries(CharSequence entries[], CharSequence[] contentDescriptions)1862 private SpannableString[] createAccessibleEntries(CharSequence entries[], 1863 CharSequence[] contentDescriptions) { 1864 final SpannableString[] accessibleEntries = new SpannableString[entries.length]; 1865 for (int i = 0; i < entries.length; i++) { 1866 accessibleEntries[i] = com.android.settings.Utils.createAccessibleSequence(entries[i], 1867 contentDescriptions[i].toString()); 1868 } 1869 return accessibleEntries; 1870 } 1871 hideSoftKeyboard(IBinder windowToken)1872 private void hideSoftKeyboard(IBinder windowToken) { 1873 final InputMethodManager inputMethodManager = mContext.getSystemService( 1874 InputMethodManager.class); 1875 inputMethodManager.hideSoftInputFromWindow(windowToken, 0 /* flags */); 1876 } 1877 setAdvancedOptionAccessibilityString()1878 private void setAdvancedOptionAccessibilityString() { 1879 final CheckBox advancedToggleBox = mView.findViewById(R.id.wifi_advanced_togglebox); 1880 advancedToggleBox.setAccessibilityDelegate(new AccessibilityDelegate() { 1881 @Override 1882 public void onInitializeAccessibilityNodeInfo( 1883 View v, AccessibilityNodeInfo info) { 1884 super.onInitializeAccessibilityNodeInfo(v, info); 1885 // To let TalkBack don't pronounce checked/unchecked. 1886 info.setCheckable(false /* checkable */); 1887 // To let TalkBack don't pronounce CheckBox. 1888 info.setClassName(null /* className */); 1889 // Customize TalkBack's pronunciation which been appended to "Double-tap to". 1890 final AccessibilityAction customClick = new AccessibilityAction( 1891 AccessibilityNodeInfo.ACTION_CLICK, 1892 mContext.getString(R.string.wifi_advanced_toggle_description_collapsed)); 1893 info.addAction(customClick); 1894 } 1895 }); 1896 } 1897 } 1898