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