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