| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.settings.wifi; |
| |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.net.InetAddresses; |
| import android.net.IpConfiguration; |
| import android.net.IpConfiguration.IpAssignment; |
| import android.net.IpConfiguration.ProxySettings; |
| import android.net.LinkAddress; |
| import android.net.ProxyInfo; |
| import android.net.StaticIpConfiguration; |
| import android.net.Uri; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiEnterpriseConfig; |
| import android.net.wifi.WifiEnterpriseConfig.Eap; |
| import android.net.wifi.WifiEnterpriseConfig.Phase2; |
| import android.net.wifi.WifiManager; |
| import android.os.IBinder; |
| import android.security.keystore.KeyProperties; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.text.Editable; |
| import android.text.InputType; |
| import android.text.SpannableString; |
| import android.text.TextUtils; |
| import android.text.TextWatcher; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.View.AccessibilityDelegate; |
| import android.view.ViewGroup; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityNodeInfo; |
| import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; |
| import android.view.inputmethod.EditorInfo; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.AdapterView; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.CheckBox; |
| import android.widget.CompoundButton; |
| import android.widget.CompoundButton.OnCheckedChangeListener; |
| import android.widget.EditText; |
| import android.widget.ImageButton; |
| import android.widget.Spinner; |
| import android.widget.TextView; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.net.module.util.NetUtils; |
| import com.android.net.module.util.ProxyUtils; |
| import com.android.settings.ProxySelector; |
| import com.android.settings.R; |
| import com.android.settings.network.SubscriptionUtil; |
| import com.android.settings.utils.AndroidKeystoreAliasLoader; |
| import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2; |
| import com.android.settings.wifi.dpp.WifiDppUtils; |
| import com.android.settingslib.Utils; |
| import com.android.settingslib.utils.ThreadUtils; |
| import com.android.wifitrackerlib.WifiEntry; |
| import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; |
| |
| import java.net.Inet4Address; |
| import java.net.InetAddress; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| /** |
| * The class for allowing UIs like {@link WifiDialog2} and {@link WifiConfigUiBase2} to |
| * share the logic for controlling buttons, text fields, etc. |
| */ |
| public class WifiConfigController2 implements TextWatcher, |
| AdapterView.OnItemSelectedListener, OnCheckedChangeListener, |
| TextView.OnEditorActionListener, View.OnKeyListener { |
| private static final String TAG = "WifiConfigController2"; |
| @VisibleForTesting |
| static final String DEFAULT_ANONYMOUS_ID = "anonymous"; |
| |
| private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; |
| |
| private final WifiConfigUiBase2 mConfigUi; |
| private final View mView; |
| private final WifiEntry mWifiEntry; |
| |
| /* This value comes from "wifi_ip_settings" resource array */ |
| private static final int DHCP = 0; |
| private static final int STATIC_IP = 1; |
| |
| /* Constants used for referring to the hidden state of a network. */ |
| public static final int HIDDEN_NETWORK = 1; |
| public static final int NOT_HIDDEN_NETWORK = 0; |
| |
| /* These values come from "wifi_proxy_settings" resource array */ |
| public static final int PROXY_NONE = 0; |
| public static final int PROXY_STATIC = 1; |
| public static final int PROXY_PAC = 2; |
| |
| /* These values come from "wifi_eap_method" resource array */ |
| public static final int WIFI_EAP_METHOD_PEAP = 0; |
| public static final int WIFI_EAP_METHOD_TLS = 1; |
| public static final int WIFI_EAP_METHOD_TTLS = 2; |
| public static final int WIFI_EAP_METHOD_PWD = 3; |
| public static final int WIFI_EAP_METHOD_SIM = 4; |
| public static final int WIFI_EAP_METHOD_AKA = 5; |
| public static final int WIFI_EAP_METHOD_AKA_PRIME = 6; |
| |
| /* These values come from "wifi_peap_phase2_entries" resource array */ |
| public static final int WIFI_PEAP_PHASE2_MSCHAPV2 = 0; |
| public static final int WIFI_PEAP_PHASE2_GTC = 1; |
| public static final int WIFI_PEAP_PHASE2_SIM = 2; |
| public static final int WIFI_PEAP_PHASE2_AKA = 3; |
| public static final int WIFI_PEAP_PHASE2_AKA_PRIME = 4; |
| |
| /* These values come from "wifi_ttls_phase2_entries" resource array */ |
| public static final int WIFI_TTLS_PHASE2_PAP = 0; |
| public static final int WIFI_TTLS_PHASE2_MSCHAP = 1; |
| public static final int WIFI_TTLS_PHASE2_MSCHAPV2 = 2; |
| public static final int WIFI_TTLS_PHASE2_GTC = 3; |
| |
| private static final String UNDESIRED_CERTIFICATE_MACRANDSECRET = "MacRandSecret"; |
| private static final String UNDESIRED_CERTIFICATE_MACRANDSAPSECRET = "MacRandSapSecret"; |
| @VisibleForTesting |
| static final String[] UNDESIRED_CERTIFICATES = { |
| UNDESIRED_CERTIFICATE_MACRANDSECRET, |
| UNDESIRED_CERTIFICATE_MACRANDSAPSECRET |
| }; |
| |
| /* These values are for install certificate */ |
| private static final String ACTION_INSTALL_CERTS = "android.credentials.INSTALL"; |
| private static final String PACKAGE_INSTALL_CERTS = "com.android.certinstaller"; |
| private static final String CLASS_INSTALL_CERTS = "com.android.certinstaller.CertInstallerMain"; |
| private static final String KEY_INSTALL_CERTIFICATE = "certificate_install_usage"; |
| private static final String INSTALL_CERTIFICATE_VALUE = "wifi"; |
| |
| protected int REQUEST_INSTALL_CERTS = 1; |
| |
| /* Phase2 methods supported by PEAP are limited */ |
| private ArrayAdapter<CharSequence> mPhase2PeapAdapter; |
| /* Phase2 methods supported by TTLS are limited */ |
| private ArrayAdapter<CharSequence> mPhase2TtlsAdapter; |
| |
| // e.g. WifiEntry.SECURITY_NONE |
| @VisibleForTesting |
| int mWifiEntrySecurity; |
| private TextView mPasswordView; |
| private ImageButton mSsidScanButton; |
| |
| private String mUnspecifiedCertString; |
| private String mMultipleCertSetString; |
| private String mUseSystemCertsString; |
| private String mTrustOnFirstUse; |
| private String mDoNotProvideEapUserCertString; |
| @VisibleForTesting String mInstallCertsString; |
| |
| private Spinner mSecuritySpinner; |
| @VisibleForTesting Spinner mEapMethodSpinner; |
| private int mLastShownEapMethod; |
| @VisibleForTesting Spinner mEapSimSpinner; // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME. |
| @VisibleForTesting Spinner mEapCaCertSpinner; |
| private Spinner mEapMinTlsVerSpinner; |
| private Spinner mEapOcspSpinner; |
| private TextView mEapDomainView; |
| private Spinner mPhase2Spinner; |
| // Associated with mPhase2Spinner, one of mPhase2TtlsAdapter or mPhase2PeapAdapter |
| private ArrayAdapter<CharSequence> mPhase2Adapter; |
| private Spinner mEapUserCertSpinner; |
| private TextView mEapIdentityView; |
| @VisibleForTesting |
| TextView mEapAnonymousView; |
| |
| private Spinner mIpSettingsSpinner; |
| private TextView mIpAddressView; |
| private TextView mGatewayView; |
| private TextView mNetworkPrefixLengthView; |
| private TextView mDns1View; |
| private TextView mDns2View; |
| |
| private Spinner mProxySettingsSpinner; |
| private Spinner mMeteredSettingsSpinner; |
| private Spinner mHiddenSettingsSpinner; |
| private Spinner mPrivacySettingsSpinner; |
| private TextView mHiddenWarningView; |
| private TextView mProxyHostView; |
| private TextView mProxyPortView; |
| private TextView mProxyExclusionListView; |
| private TextView mProxyPacView; |
| private CheckBox mSharedCheckBox; |
| |
| private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED; |
| private ProxySettings mProxySettings = ProxySettings.UNASSIGNED; |
| private ProxyInfo mHttpProxy = null; |
| private StaticIpConfiguration mStaticIpConfiguration = null; |
| |
| private String[] mLevels; |
| private int mMode; |
| private TextView mSsidView; |
| |
| private Context mContext; |
| |
| @VisibleForTesting |
| Integer[] mSecurityInPosition; |
| |
| private final WifiManager mWifiManager; |
| private boolean mIsTrustOnFirstUseSupported; |
| |
| private final ArrayMap<Integer, SubscriptionInfo> mActiveSubscriptionInfos = new ArrayMap<>(); |
| |
| public WifiConfigController2(WifiConfigUiBase2 parent, View view, WifiEntry wifiEntry, |
| int mode) { |
| mConfigUi = parent; |
| mView = view; |
| mWifiEntry = wifiEntry; |
| mContext = mConfigUi.getContext(); |
| |
| // Init Wi-Fi manager |
| mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); |
| initWifiConfigController2(wifiEntry, mode); |
| } |
| |
| @VisibleForTesting |
| public WifiConfigController2(WifiConfigUiBase2 parent, View view, WifiEntry wifiEntry, |
| int mode, WifiManager wifiManager) { |
| mConfigUi = parent; |
| |
| mView = view; |
| mWifiEntry = wifiEntry; |
| mContext = mConfigUi.getContext(); |
| mWifiManager = wifiManager; |
| initWifiConfigController2(wifiEntry, mode); |
| } |
| |
| private void initWifiConfigController2(WifiEntry wifiEntry, int mode) { |
| |
| mWifiEntrySecurity = (wifiEntry == null) ? WifiEntry.SECURITY_NONE : |
| wifiEntry.getSecurity(); |
| mMode = mode; |
| mIsTrustOnFirstUseSupported = mWifiManager.isTrustOnFirstUseSupported(); |
| |
| final Resources res = mContext.getResources(); |
| |
| mLevels = res.getStringArray(R.array.wifi_signal); |
| if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_eap_sim_based_auth_supported)) { |
| mPhase2PeapAdapter = getSpinnerAdapter(R.array.wifi_peap_phase2_entries); |
| } else { |
| mPhase2PeapAdapter = getSpinnerAdapterWithEapMethodsTts( |
| R.array.wifi_peap_phase2_entries_with_sim_auth); |
| } |
| |
| mPhase2TtlsAdapter = getSpinnerAdapter(R.array.wifi_ttls_phase2_entries); |
| |
| mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified); |
| mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added); |
| mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs); |
| mTrustOnFirstUse = mContext.getString(R.string.wifi_trust_on_first_use); |
| mDoNotProvideEapUserCertString = |
| mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); |
| mInstallCertsString = mContext.getString(R.string.wifi_install_credentials); |
| |
| mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button); |
| mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings); |
| mIpSettingsSpinner.setOnItemSelectedListener(this); |
| mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings); |
| mProxySettingsSpinner.setOnItemSelectedListener(this); |
| mSharedCheckBox = (CheckBox) mView.findViewById(R.id.shared); |
| mMeteredSettingsSpinner = mView.findViewById(R.id.metered_settings); |
| mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings); |
| mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings); |
| if (mWifiManager.isConnectedMacRandomizationSupported()) { |
| View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields); |
| privacySettingsLayout.setVisibility(View.VISIBLE); |
| } |
| mHiddenSettingsSpinner.setOnItemSelectedListener(this); |
| mHiddenWarningView = mView.findViewById(R.id.hidden_settings_warning); |
| mHiddenWarningView.setVisibility( |
| mHiddenSettingsSpinner.getSelectedItemPosition() == NOT_HIDDEN_NETWORK |
| ? View.GONE |
| : View.VISIBLE); |
| mSecurityInPosition = new Integer[WifiEntry.NUM_SECURITY_TYPES]; |
| |
| if (mWifiEntry == null) { // new network |
| configureSecuritySpinner(); |
| mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); |
| } else { |
| mConfigUi.setTitle(mWifiEntry.getTitle()); |
| |
| ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); |
| |
| boolean showAdvancedFields = false; |
| if (mWifiEntry.isSaved()) { |
| WifiConfiguration config = mWifiEntry.getWifiConfiguration(); |
| mMeteredSettingsSpinner.setSelection(config.meteredOverride); |
| mHiddenSettingsSpinner.setSelection(config.hiddenSSID |
| ? HIDDEN_NETWORK |
| : NOT_HIDDEN_NETWORK); |
| |
| final int prefMacValue = WifiPrivacyPreferenceController2 |
| .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting); |
| mPrivacySettingsSpinner.setSelection(prefMacValue); |
| |
| if (config.getIpConfiguration().getIpAssignment() == IpAssignment.STATIC) { |
| mIpSettingsSpinner.setSelection(STATIC_IP); |
| showAdvancedFields = true; |
| // Display IP address. |
| StaticIpConfiguration staticConfig = config.getIpConfiguration() |
| .getStaticIpConfiguration(); |
| if (staticConfig != null && staticConfig.getIpAddress() != null) { |
| addRow(group, R.string.wifi_ip_address, |
| staticConfig.getIpAddress().getAddress().getHostAddress()); |
| } |
| } else { |
| mIpSettingsSpinner.setSelection(DHCP); |
| } |
| |
| mSharedCheckBox.setEnabled(config.shared); |
| if (!config.shared) { |
| showAdvancedFields = true; |
| } |
| |
| ProxySettings proxySettings = config.getIpConfiguration().getProxySettings(); |
| if (proxySettings == ProxySettings.STATIC) { |
| mProxySettingsSpinner.setSelection(PROXY_STATIC); |
| showAdvancedFields = true; |
| } else if (proxySettings == ProxySettings.PAC) { |
| mProxySettingsSpinner.setSelection(PROXY_PAC); |
| showAdvancedFields = true; |
| } else { |
| mProxySettingsSpinner.setSelection(PROXY_NONE); |
| } |
| if (config != null && config.isPasspoint()) { |
| addRow(group, R.string.passpoint_label, |
| String.format(mContext.getString(R.string.passpoint_content), |
| config.providerFriendlyName)); |
| } |
| } |
| |
| if ((!mWifiEntry.isSaved() |
| && mWifiEntry.getConnectedState() != WifiEntry.CONNECTED_STATE_CONNECTED |
| && !mWifiEntry.isSubscription()) |
| || mMode != WifiConfigUiBase2.MODE_VIEW) { |
| showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true); |
| showIpConfigFields(); |
| showProxyFields(); |
| final CheckBox advancedTogglebox = |
| (CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox); |
| if (!showAdvancedFields) { |
| // Need to show Advanced Option button. |
| mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); |
| advancedTogglebox.setOnCheckedChangeListener(this); |
| advancedTogglebox.setChecked(showAdvancedFields); |
| setAdvancedOptionAccessibilityString(); |
| } |
| mView.findViewById(R.id.wifi_advanced_fields) |
| .setVisibility(showAdvancedFields ? View.VISIBLE : View.GONE); |
| } |
| |
| if (mMode == WifiConfigUiBase2.MODE_MODIFY) { |
| mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); |
| } else if (mMode == WifiConfigUiBase2.MODE_CONNECT) { |
| mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); |
| } else { |
| final String signalLevel = getSignalString(); |
| |
| if (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED |
| && signalLevel != null) { |
| mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); |
| } else { |
| if (signalLevel != null) { |
| addRow(group, R.string.wifi_signal, signalLevel); |
| } |
| |
| final ConnectedInfo info = mWifiEntry.getConnectedInfo(); |
| if (info != null && info.linkSpeedMbps >= 0) { |
| addRow(group, R.string.wifi_speed, String.format( |
| res.getString(R.string.link_speed), info.linkSpeedMbps)); |
| } |
| |
| if (info != null && info.frequencyMhz != WifiEntry.FREQUENCY_UNKNOWN) { |
| final int frequency = info.frequencyMhz; |
| String band = null; |
| |
| if (frequency >= WifiEntry.MIN_FREQ_24GHZ |
| && frequency < WifiEntry.MAX_FREQ_24GHZ) { |
| band = res.getString(R.string.wifi_band_24ghz); |
| } else if (frequency >= WifiEntry.MIN_FREQ_5GHZ |
| && frequency < WifiEntry.MAX_FREQ_5GHZ) { |
| band = res.getString(R.string.wifi_band_5ghz); |
| } else if (frequency >= WifiEntry.MIN_FREQ_6GHZ |
| && frequency < WifiEntry.MAX_FREQ_6GHZ) { |
| band = res.getString(R.string.wifi_band_6ghz); |
| } else { |
| Log.e(TAG, "Unexpected frequency " + frequency); |
| } |
| if (band != null) { |
| addRow(group, R.string.wifi_frequency, band); |
| } |
| } |
| |
| addRow(group, R.string.wifi_security, |
| mWifiEntry.getSecurityString(false /* concise */)); |
| mView.findViewById(R.id.ip_fields).setVisibility(View.GONE); |
| } |
| if (mWifiEntry.isSaved() |
| || mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED |
| || mWifiEntry.isSubscription()) { |
| mConfigUi.setForgetButton(res.getString(R.string.wifi_forget)); |
| } |
| } |
| |
| mSsidScanButton.setVisibility(View.GONE); |
| } |
| mSharedCheckBox.setVisibility(View.GONE); |
| |
| mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel)); |
| if (mConfigUi.getSubmitButton() != null) { |
| enableSubmitIfAppropriate(); |
| } |
| |
| // After done view show and hide, request focus |
| mView.findViewById(R.id.l_wifidialog).requestFocus(); |
| } |
| |
| private void addRow(ViewGroup group, int name, String value) { |
| View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false); |
| ((TextView) row.findViewById(R.id.name)).setText(name); |
| ((TextView) row.findViewById(R.id.value)).setText(value); |
| group.addView(row); |
| } |
| |
| @VisibleForTesting |
| String getSignalString() { |
| if (mWifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) { |
| return null; |
| } |
| final int level = mWifiEntry.getLevel(); |
| |
| return (level > -1 && level < mLevels.length) ? mLevels[level] : null; |
| } |
| |
| void hideForgetButton() { |
| Button forget = mConfigUi.getForgetButton(); |
| if (forget == null) return; |
| |
| forget.setVisibility(View.GONE); |
| } |
| |
| void hideSubmitButton() { |
| Button submit = mConfigUi.getSubmitButton(); |
| if (submit == null) return; |
| |
| submit.setVisibility(View.GONE); |
| } |
| |
| /* show submit button if password, ip and proxy settings are valid */ |
| void enableSubmitIfAppropriate() { |
| Button submit = mConfigUi.getSubmitButton(); |
| if (submit == null) return; |
| |
| submit.setEnabled(isSubmittable()); |
| } |
| |
| boolean isValidPsk(String password) { |
| if (password.length() == 64 && password.matches("[0-9A-Fa-f]{64}")) { |
| return true; |
| } else if (password.length() >= 8 && password.length() <= 63) { |
| return true; |
| } |
| return false; |
| } |
| |
| boolean isValidSaePassword(String password) { |
| if (password.length() >= 1 && password.length() <= 128) { |
| return true; |
| } |
| return false; |
| } |
| |
| boolean isSubmittable() { |
| boolean enabled = false; |
| boolean passwordInvalid = false; |
| if (mPasswordView != null |
| && ((mWifiEntrySecurity == WifiEntry.SECURITY_WEP |
| && mPasswordView.length() == 0) |
| || (mWifiEntrySecurity == WifiEntry.SECURITY_PSK |
| && !isValidPsk(mPasswordView.getText().toString())) |
| || (mWifiEntrySecurity == WifiEntry.SECURITY_SAE |
| && !isValidSaePassword(mPasswordView.getText().toString())))) { |
| passwordInvalid = true; |
| } |
| if ((mSsidView != null && mSsidView.length() == 0) |
| // If WifiEntry is not saved, apply passwordInvalid check |
| || ((mWifiEntry == null || !mWifiEntry.isSaved()) && passwordInvalid |
| // If WifiEntry is saved (modifying network) and password is changed, apply |
| // Invalid password check |
| || mWifiEntry != null && mWifiEntry.isSaved() && passwordInvalid |
| && mPasswordView.length() > 0)) { |
| enabled = false; |
| } else { |
| enabled = ipAndProxyFieldsAreValid(); |
| } |
| if ((mWifiEntrySecurity == WifiEntry.SECURITY_EAP |
| || mWifiEntrySecurity == WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE |
| || mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) |
| && mEapCaCertSpinner != null |
| && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { |
| String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); |
| if (caCertSelection.equals(mUnspecifiedCertString)) { |
| // Disallow submit if the user has not selected a CA certificate for an EAP network |
| // configuration. |
| enabled = false; |
| } else if (mEapDomainView != null |
| && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE |
| && TextUtils.isEmpty(mEapDomainView.getText().toString())) { |
| // Disallow submit if the user chooses to use a certificate for EAP server |
| // validation, but does not provide a domain. |
| enabled = false; |
| } |
| } |
| if ((mWifiEntrySecurity == WifiEntry.SECURITY_EAP |
| || mWifiEntrySecurity == WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE |
| || mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) |
| && mEapUserCertSpinner != null |
| && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE |
| && mEapUserCertSpinner.getSelectedItem().equals(mUnspecifiedCertString)) { |
| // Disallow submit if the user has not selected a user certificate for an EAP network |
| // configuration. |
| enabled = false; |
| } |
| return enabled; |
| } |
| |
| void showWarningMessagesIfAppropriate() { |
| mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE); |
| mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE); |
| mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE); |
| |
| if (mSsidView != null) { |
| final String ssid = mSsidView.getText().toString(); |
| if (WifiUtils.isSSIDTooLong(ssid)) { |
| mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.VISIBLE); |
| } |
| } |
| if (mEapCaCertSpinner != null |
| && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { |
| if (mEapDomainView != null |
| && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE |
| && TextUtils.isEmpty(mEapDomainView.getText().toString())) { |
| // Display warning if user chooses to use a certificate without restricting the |
| // server domain that these certificates can be used to validate. |
| mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE); |
| } |
| } |
| |
| if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B |
| && mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_TLS) { |
| String userCertSelection = (String) mEapUserCertSpinner.getSelectedItem(); |
| if (userCertSelection.equals(mUnspecifiedCertString)) { |
| mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.VISIBLE); |
| } |
| } |
| } |
| |
| /** |
| * @return {@link WifiConfiguration} from mWifiEntry and UI edit result. |
| */ |
| public WifiConfiguration getConfig() { |
| if (mMode == WifiConfigUiBase2.MODE_VIEW) { |
| return null; |
| } |
| |
| WifiConfiguration config; |
| if (mWifiEntry == null) { |
| config = new WifiConfiguration(); |
| config.SSID = "\"" + mSsidView.getText().toString() + "\""; |
| // If the user adds a network manually, assume that it is hidden. |
| config.hiddenSSID = mHiddenSettingsSpinner.getSelectedItemPosition() == HIDDEN_NETWORK; |
| } else if (mWifiEntry.isSaved()) { |
| config = new WifiConfiguration(mWifiEntry.getWifiConfiguration()); |
| } else { |
| config = new WifiConfiguration(); |
| config.SSID = "\"" + mWifiEntry.getTitle() + "\""; |
| } |
| |
| config.shared = mSharedCheckBox.isChecked(); |
| |
| switch (mWifiEntrySecurity) { |
| case WifiEntry.SECURITY_NONE: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); |
| } |
| break; |
| |
| case WifiEntry.SECURITY_WEP: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); |
| } |
| if (mPasswordView.length() != 0) { |
| int length = mPasswordView.length(); |
| String password = mPasswordView.getText().toString(); |
| // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) |
| if ((length == 10 || length == 26 || length == 58) |
| && password.matches("[0-9A-Fa-f]*")) { |
| config.wepKeys[0] = password; |
| } else { |
| config.wepKeys[0] = '"' + password + '"'; |
| } |
| } |
| break; |
| |
| case WifiEntry.SECURITY_PSK: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); |
| } |
| if (mPasswordView.length() != 0) { |
| String password = mPasswordView.getText().toString(); |
| if (password.matches("[0-9A-Fa-f]{64}")) { |
| config.preSharedKey = password; |
| } else { |
| config.preSharedKey = '"' + password + '"'; |
| } |
| } |
| break; |
| |
| case WifiEntry.SECURITY_EAP: |
| case WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE: |
| case WifiEntry.SECURITY_EAP_SUITE_B: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) { |
| // allowedSuiteBCiphers will be set according to certificate type |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); |
| } else if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE) { |
| config.setSecurityParams( |
| WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); |
| } else { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); |
| } |
| } |
| config.enterpriseConfig = new WifiEnterpriseConfig(); |
| int eapMethod = mEapMethodSpinner.getSelectedItemPosition(); |
| int phase2Method = mPhase2Spinner.getSelectedItemPosition(); |
| config.enterpriseConfig.setEapMethod(eapMethod); |
| switch (eapMethod) { |
| case Eap.PEAP: |
| // PEAP supports limited phase2 values |
| // Map the index from the mPhase2PeapAdapter to the one used |
| // by the API which has the full list of PEAP methods. |
| switch(phase2Method) { |
| case WIFI_PEAP_PHASE2_MSCHAPV2: |
| config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); |
| break; |
| case WIFI_PEAP_PHASE2_GTC: |
| config.enterpriseConfig.setPhase2Method(Phase2.GTC); |
| break; |
| case WIFI_PEAP_PHASE2_SIM: |
| config.enterpriseConfig.setPhase2Method(Phase2.SIM); |
| break; |
| case WIFI_PEAP_PHASE2_AKA: |
| config.enterpriseConfig.setPhase2Method(Phase2.AKA); |
| break; |
| case WIFI_PEAP_PHASE2_AKA_PRIME: |
| config.enterpriseConfig.setPhase2Method(Phase2.AKA_PRIME); |
| break; |
| default: |
| Log.e(TAG, "Unknown phase2 method" + phase2Method); |
| break; |
| } |
| break; |
| case Eap.TTLS: |
| // The default index from mPhase2TtlsAdapter maps to the API |
| switch(phase2Method) { |
| case WIFI_TTLS_PHASE2_PAP: |
| config.enterpriseConfig.setPhase2Method(Phase2.PAP); |
| break; |
| case WIFI_TTLS_PHASE2_MSCHAP: |
| config.enterpriseConfig.setPhase2Method(Phase2.MSCHAP); |
| break; |
| case WIFI_TTLS_PHASE2_MSCHAPV2: |
| config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); |
| break; |
| case WIFI_TTLS_PHASE2_GTC: |
| config.enterpriseConfig.setPhase2Method(Phase2.GTC); |
| break; |
| default: |
| Log.e(TAG, "Unknown phase2 method" + phase2Method); |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (config.enterpriseConfig.isAuthenticationSimBased() |
| && mActiveSubscriptionInfos.size() > 0) { |
| config.carrierId = mActiveSubscriptionInfos |
| .valueAt(mEapSimSpinner.getSelectedItemPosition()).getCarrierId(); |
| } |
| |
| String caCert = (String) mEapCaCertSpinner.getSelectedItem(); |
| config.enterpriseConfig.setCaCertificateAliases(null); |
| config.enterpriseConfig.setCaPath(null); |
| config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString()); |
| if (caCert.equals(mUnspecifiedCertString)) { |
| // ca_cert already set to null, so do nothing. |
| } else if (mIsTrustOnFirstUseSupported && caCert.equals(mTrustOnFirstUse)) { |
| config.enterpriseConfig.enableTrustOnFirstUse(true); |
| } else if (caCert.equals(mUseSystemCertsString)) { |
| config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH); |
| } else if (caCert.equals(mMultipleCertSetString)) { |
| if (mWifiEntry != null) { |
| if (!mWifiEntry.isSaved()) { |
| Log.e(TAG, "Multiple certs can only be set " |
| + "when editing saved network"); |
| } |
| config.enterpriseConfig.setCaCertificateAliases( |
| mWifiEntry.getWifiConfiguration() |
| .enterpriseConfig |
| .getCaCertificateAliases()); |
| } |
| } else { |
| config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert}); |
| } |
| |
| // ca_cert or ca_path should not both be non-null, since we only intend to let |
| // the use either their own certificate, or the system certificates, not both. |
| // The variable that is not used must explicitly be set to null, so that a |
| // previously-set value on a saved configuration will be erased on an update. |
| if (config.enterpriseConfig.getCaCertificateAliases() != null |
| && config.enterpriseConfig.getCaPath() != null) { |
| Log.e(TAG, "ca_cert (" |
| + Arrays.toString(config.enterpriseConfig.getCaCertificateAliases()) |
| + ") and ca_path (" |
| + config.enterpriseConfig.getCaPath() |
| + ") should not both be non-null"); |
| } |
| |
| // Only set certificate option if there is a valid CA certificate. |
| if (caCert.equals(mUnspecifiedCertString)) { |
| config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE); |
| config.enterpriseConfig.setMinimumTlsVersion(WifiEnterpriseConfig.TLS_V1_0); |
| } else { |
| config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition()); |
| config.enterpriseConfig.setMinimumTlsVersion( |
| mEapMinTlsVerSpinner.getSelectedItemPosition()); |
| } |
| |
| String clientCert = (String) mEapUserCertSpinner.getSelectedItem(); |
| if (clientCert.equals(mUnspecifiedCertString) |
| || clientCert.equals(mDoNotProvideEapUserCertString)) { |
| // Note: |clientCert| should not be able to take the value |unspecifiedCert|, |
| // since we prevent such configurations from being saved. |
| clientCert = ""; |
| } |
| config.enterpriseConfig.setClientCertificateAlias(clientCert); |
| if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) { |
| config.enterpriseConfig.setIdentity(""); |
| config.enterpriseConfig.setAnonymousIdentity(""); |
| } else if (eapMethod == Eap.PWD) { |
| config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString()); |
| config.enterpriseConfig.setAnonymousIdentity(""); |
| } else { |
| config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString()); |
| config.enterpriseConfig.setAnonymousIdentity( |
| mEapAnonymousView.getText().toString()); |
| } |
| |
| if (mPasswordView.isShown()) { |
| // For security reasons, a previous password is not displayed to user. |
| // Update only if it has been changed. |
| if (mPasswordView.length() > 0) { |
| config.enterpriseConfig.setPassword(mPasswordView.getText().toString()); |
| } |
| } else { |
| // clear password |
| config.enterpriseConfig.setPassword(mPasswordView.getText().toString()); |
| } |
| break; |
| case WifiEntry.SECURITY_SAE: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); |
| } |
| if (mPasswordView.length() != 0) { |
| String password = mPasswordView.getText().toString(); |
| config.preSharedKey = '"' + password + '"'; |
| } |
| break; |
| |
| case WifiEntry.SECURITY_OWE: |
| if (mWifiEntry == null || !mWifiEntry.isSaved()) { |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); |
| } |
| break; |
| |
| default: |
| return null; |
| } |
| |
| final IpConfiguration ipConfig = new IpConfiguration(); |
| ipConfig.setIpAssignment(mIpAssignment); |
| ipConfig.setProxySettings(mProxySettings); |
| ipConfig.setStaticIpConfiguration(mStaticIpConfiguration); |
| ipConfig.setHttpProxy(mHttpProxy); |
| config.setIpConfiguration(ipConfig); |
| if (mMeteredSettingsSpinner != null) { |
| config.meteredOverride = mMeteredSettingsSpinner.getSelectedItemPosition(); |
| } |
| |
| if (mPrivacySettingsSpinner != null) { |
| config.macRandomizationSetting = WifiPrivacyPreferenceController2 |
| .translatePrefValueToMacRandomizedValue(mPrivacySettingsSpinner |
| .getSelectedItemPosition()); |
| } |
| |
| return config; |
| } |
| |
| private boolean ipAndProxyFieldsAreValid() { |
| mIpAssignment = |
| (mIpSettingsSpinner != null |
| && mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) |
| ? IpAssignment.STATIC |
| : IpAssignment.DHCP; |
| |
| if (mIpAssignment == IpAssignment.STATIC) { |
| mStaticIpConfiguration = new StaticIpConfiguration(); |
| int result = validateIpConfigFields(mStaticIpConfiguration); |
| if (result != 0) { |
| return false; |
| } |
| } |
| |
| final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition(); |
| mProxySettings = ProxySettings.NONE; |
| mHttpProxy = null; |
| if (selectedPosition == PROXY_STATIC && mProxyHostView != null) { |
| mProxySettings = ProxySettings.STATIC; |
| String host = mProxyHostView.getText().toString(); |
| String portStr = mProxyPortView.getText().toString(); |
| String exclusionList = mProxyExclusionListView.getText().toString(); |
| int port = 0; |
| int result = 0; |
| try { |
| port = Integer.parseInt(portStr); |
| result = ProxySelector.validate(host, portStr, exclusionList); |
| } catch (NumberFormatException e) { |
| result = R.string.proxy_error_invalid_port; |
| } |
| if (result == 0) { |
| mHttpProxy = ProxyInfo.buildDirectProxy( |
| host, port, Arrays.asList(exclusionList.split(","))); |
| } else { |
| return false; |
| } |
| } else if (selectedPosition == PROXY_PAC && mProxyPacView != null) { |
| mProxySettings = ProxySettings.PAC; |
| CharSequence uriSequence = mProxyPacView.getText(); |
| if (TextUtils.isEmpty(uriSequence)) { |
| return false; |
| } |
| Uri uri = Uri.parse(uriSequence.toString()); |
| if (uri == null) { |
| return false; |
| } |
| mHttpProxy = ProxyInfo.buildPacProxy(uri); |
| } |
| return true; |
| } |
| |
| private Inet4Address getIPv4Address(String text) { |
| try { |
| return (Inet4Address) InetAddresses.parseNumericAddress(text); |
| } catch (IllegalArgumentException | ClassCastException e) { |
| return null; |
| } |
| } |
| |
| private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) { |
| if (mIpAddressView == null) return 0; |
| |
| String ipAddr = mIpAddressView.getText().toString(); |
| if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address; |
| |
| Inet4Address inetAddr = getIPv4Address(ipAddr); |
| if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) { |
| return R.string.wifi_ip_settings_invalid_ip_address; |
| } |
| |
| // Copy all fields into the builder first and set desired value later with builder. |
| final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder() |
| .setDnsServers(staticIpConfiguration.getDnsServers()) |
| .setDomains(staticIpConfiguration.getDomains()) |
| .setGateway(staticIpConfiguration.getGateway()) |
| .setIpAddress(staticIpConfiguration.getIpAddress()); |
| try { |
| int networkPrefixLength = -1; |
| try { |
| networkPrefixLength = |
| Integer.parseInt(mNetworkPrefixLengthView.getText().toString()); |
| if (networkPrefixLength < 0 || networkPrefixLength > 32) { |
| return R.string.wifi_ip_settings_invalid_network_prefix_length; |
| } |
| staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength)); |
| } catch (NumberFormatException e) { |
| // Set the hint as default after user types in ip address |
| mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( |
| R.string.wifi_network_prefix_length_hint)); |
| } catch (IllegalArgumentException e) { |
| return R.string.wifi_ip_settings_invalid_ip_address; |
| } |
| |
| String gateway = mGatewayView.getText().toString(); |
| if (TextUtils.isEmpty(gateway)) { |
| try { |
| //Extract a default gateway from IP address |
| InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); |
| byte[] addr = netPart.getAddress(); |
| addr[addr.length - 1] = 1; |
| mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); |
| } catch (RuntimeException ee) { |
| } catch (java.net.UnknownHostException u) { |
| } |
| } else { |
| InetAddress gatewayAddr = getIPv4Address(gateway); |
| if (gatewayAddr == null) { |
| return R.string.wifi_ip_settings_invalid_gateway; |
| } |
| if (gatewayAddr.isMulticastAddress()) { |
| return R.string.wifi_ip_settings_invalid_gateway; |
| } |
| staticIPBuilder.setGateway(gatewayAddr); |
| } |
| |
| String dns = mDns1View.getText().toString(); |
| InetAddress dnsAddr = null; |
| final ArrayList<InetAddress> dnsServers = new ArrayList<>(); |
| |
| if (TextUtils.isEmpty(dns)) { |
| //If everything else is valid, provide hint as a default option |
| mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); |
| } else { |
| dnsAddr = getIPv4Address(dns); |
| if (dnsAddr == null) { |
| return R.string.wifi_ip_settings_invalid_dns; |
| } |
| dnsServers.add(dnsAddr); |
| } |
| |
| if (mDns2View.length() > 0) { |
| dns = mDns2View.getText().toString(); |
| dnsAddr = getIPv4Address(dns); |
| if (dnsAddr == null) { |
| return R.string.wifi_ip_settings_invalid_dns; |
| } |
| dnsServers.add(dnsAddr); |
| } |
| staticIPBuilder.setDnsServers(dnsServers); |
| return 0; |
| } finally { |
| // Caller of this method may rely on staticIpConfiguration, so build the final result |
| // at the end of the method. |
| mStaticIpConfiguration = staticIPBuilder.build(); |
| } |
| } |
| |
| protected void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) { |
| if (mWifiEntrySecurity == WifiEntry.SECURITY_NONE |
| || mWifiEntrySecurity == WifiEntry.SECURITY_OWE) { |
| mView.findViewById(R.id.security_fields).setVisibility(View.GONE); |
| return; |
| } |
| mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE); |
| |
| if (mPasswordView == null) { |
| mPasswordView = (TextView) mView.findViewById(R.id.password); |
| mPasswordView.addTextChangedListener(this); |
| mPasswordView.setOnEditorActionListener(this); |
| mPasswordView.setOnKeyListener(this); |
| ((CheckBox) mView.findViewById(R.id.show_password)) |
| .setOnCheckedChangeListener(this); |
| |
| if (mWifiEntry != null && mWifiEntry.isSaved()) { |
| mPasswordView.setHint(R.string.wifi_unchanged); |
| } |
| } |
| |
| if (mWifiEntrySecurity != WifiEntry.SECURITY_EAP |
| && mWifiEntrySecurity != WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE |
| && mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) { |
| mView.findViewById(R.id.eap).setVisibility(View.GONE); |
| return; |
| } |
| mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); |
| |
| // TODO (b/140541213): Maybe we can remove initiateEnterpriseNetworkUi by moving code block |
| boolean initiateEnterpriseNetworkUi = false; |
| if (mEapMethodSpinner == null) { |
| initiateEnterpriseNetworkUi = true; |
| mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method); |
| mEapMethodSpinner.setOnItemSelectedListener(this); |
| mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim); |
| mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2); |
| mPhase2Spinner.setOnItemSelectedListener(this); |
| mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert); |
| mEapCaCertSpinner.setOnItemSelectedListener(this); |
| mEapMinTlsVerSpinner = getEapMinTlsVerSpinner(mWifiManager.isTlsV13Supported()); |
| |
| mEapOcspSpinner = (Spinner) mView.findViewById(R.id.ocsp); |
| mEapDomainView = (TextView) mView.findViewById(R.id.domain); |
| mEapDomainView.addTextChangedListener(this); |
| mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert); |
| mEapUserCertSpinner.setOnItemSelectedListener(this); |
| mEapIdentityView = (TextView) mView.findViewById(R.id.identity); |
| mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous); |
| |
| setAccessibilityDelegateForSecuritySpinners(); |
| } |
| |
| if (refreshEapMethods) { |
| ArrayAdapter<CharSequence> eapMethodSpinnerAdapter; |
| if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) { |
| eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.wifi_eap_method); |
| mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); |
| // WAP3-Enterprise 192-bit only allows EAP method TLS |
| mEapMethodSpinner.setSelection(Eap.TLS); |
| mEapMethodSpinner.setEnabled(false); |
| } else if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_eap_sim_based_auth_supported)) { |
| eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.eap_method_without_sim_auth); |
| mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); |
| mEapMethodSpinner.setEnabled(true); |
| } else { |
| eapMethodSpinnerAdapter = getSpinnerAdapterWithEapMethodsTts( |
| R.array.wifi_eap_method); |
| mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter); |
| mEapMethodSpinner.setEnabled(true); |
| } |
| } |
| |
| if (refreshCertificates) { |
| loadSims(); |
| |
| final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = |
| getAndroidKeystoreAliasLoader(); |
| loadCertificates( |
| mEapCaCertSpinner, |
| androidKeystoreAliasLoader.getCaCertAliases(), |
| null /* noCertificateString */, |
| false /* showMultipleCerts */, |
| true /* showUsePreinstalledCertOption */); |
| loadCertificates( |
| mEapUserCertSpinner, |
| androidKeystoreAliasLoader.getKeyCertAliases(), |
| mDoNotProvideEapUserCertString, |
| false /* showMultipleCerts */, |
| false /* showUsePreinstalledCertOption */); |
| |
| setSelection(mEapCaCertSpinner, mUnspecifiedCertString); |
| } |
| |
| // Modifying an existing network |
| if (initiateEnterpriseNetworkUi && mWifiEntry != null && mWifiEntry.isSaved()) { |
| final WifiConfiguration wifiConfig = mWifiEntry.getWifiConfiguration(); |
| final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig; |
| final int eapMethod = enterpriseConfig.getEapMethod(); |
| final int phase2Method = enterpriseConfig.getPhase2Method(); |
| mEapMethodSpinner.setSelection(eapMethod); |
| mLastShownEapMethod = eapMethod; |
| showEapFieldsByMethod(eapMethod); |
| switch (eapMethod) { |
| case Eap.PEAP: |
| switch (phase2Method) { |
| case Phase2.MSCHAPV2: |
| mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2); |
| break; |
| case Phase2.GTC: |
| mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC); |
| break; |
| case Phase2.SIM: |
| mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_SIM); |
| break; |
| case Phase2.AKA: |
| mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA); |
| break; |
| case Phase2.AKA_PRIME: |
| mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA_PRIME); |
| break; |
| default: |
| Log.e(TAG, "Invalid phase 2 method " + phase2Method); |
| break; |
| } |
| break; |
| case Eap.TTLS: |
| switch (phase2Method) { |
| case Phase2.PAP: |
| mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_PAP); |
| break; |
| case Phase2.MSCHAP: |
| mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAP); |
| break; |
| case Phase2.MSCHAPV2: |
| mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAPV2); |
| break; |
| case Phase2.GTC: |
| mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_GTC); |
| break; |
| default: |
| Log.e(TAG, "Invalid phase 2 method " + phase2Method); |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (enterpriseConfig.isAuthenticationSimBased()) { |
| int index = mActiveSubscriptionInfos.indexOfKey(wifiConfig.carrierId); |
| if (index > -1) { |
| mEapSimSpinner.setSelection(index); |
| } |
| } |
| |
| if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) { |
| setSelection(mEapCaCertSpinner, mUseSystemCertsString); |
| } else { |
| String[] caCerts = enterpriseConfig.getCaCertificateAliases(); |
| if (caCerts == null) { |
| if (mIsTrustOnFirstUseSupported |
| && enterpriseConfig.isTrustOnFirstUseEnabled()) { |
| setSelection(mEapCaCertSpinner, mTrustOnFirstUse); |
| } else { |
| setSelection(mEapCaCertSpinner, mUnspecifiedCertString); |
| } |
| } else if (caCerts.length == 1) { |
| setSelection(mEapCaCertSpinner, caCerts[0]); |
| } else { |
| // Reload the cert spinner with an extra "multiple certificates added" item. |
| final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = |
| getAndroidKeystoreAliasLoader(); |
| loadCertificates( |
| mEapCaCertSpinner, |
| androidKeystoreAliasLoader.getCaCertAliases(), |
| null /* noCertificateString */, |
| true /* showMultipleCerts */, |
| true /* showUsePreinstalledCertOption */); |
| setSelection(mEapCaCertSpinner, mMultipleCertSetString); |
| } |
| } |
| mEapMinTlsVerSpinner.setSelection(enterpriseConfig.getMinimumTlsVersion()); |
| mEapOcspSpinner.setSelection(enterpriseConfig.getOcsp()); |
| mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch()); |
| String userCert = enterpriseConfig.getClientCertificateAlias(); |
| if (TextUtils.isEmpty(userCert)) { |
| setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString); |
| } else { |
| setSelection(mEapUserCertSpinner, userCert); |
| } |
| mEapIdentityView.setText(enterpriseConfig.getIdentity()); |
| mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity()); |
| } else { |
| showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition()); |
| } |
| } |
| |
| private void setAccessibilityDelegateForSecuritySpinners() { |
| final AccessibilityDelegate selectedEventBlocker = new AccessibilityDelegate() { |
| @Override |
| public void sendAccessibilityEvent(View host, int eventType) { |
| if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) { |
| // Ignore TYPE_VIEW_SELECTED or there will be multiple Spinner selected |
| // information for WifiController2#showSecurityFields. |
| return; |
| } |
| super.sendAccessibilityEvent(host, eventType); |
| } |
| }; |
| |
| mEapMethodSpinner.setAccessibilityDelegate(selectedEventBlocker); |
| mPhase2Spinner.setAccessibilityDelegate(selectedEventBlocker); |
| mEapCaCertSpinner.setAccessibilityDelegate(selectedEventBlocker); |
| mEapMinTlsVerSpinner.setAccessibilityDelegate(selectedEventBlocker); |
| mEapOcspSpinner.setAccessibilityDelegate(selectedEventBlocker); |
| mEapUserCertSpinner.setAccessibilityDelegate(selectedEventBlocker); |
| } |
| |
| /** |
| * EAP-PWD valid fields include |
| * identity |
| * password |
| * EAP-PEAP valid fields include |
| * phase2: MSCHAPV2, GTC, SIM, AKA, AKA' |
| * ca_cert |
| * identity |
| * anonymous_identity |
| * password (not required for SIM, AKA, AKA') |
| * EAP-TLS valid fields include |
| * user_cert |
| * ca_cert |
| * domain |
| * identity |
| * EAP-TTLS valid fields include |
| * phase2: PAP, MSCHAP, MSCHAPV2, GTC |
| * ca_cert |
| * identity |
| * anonymous_identity |
| * password |
| */ |
| private void showEapFieldsByMethod(int eapMethod) { |
| // Common defaults |
| mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE); |
| |
| // Defaults for most of the EAP methods and over-riden by |
| // by certain EAP methods |
| mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE); |
| if (mWifiManager.isTlsMinimumVersionSupported()) { |
| mView.findViewById(R.id.l_min_tls_ver).setVisibility(View.VISIBLE); |
| } |
| mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE); |
| |
| Context context = mConfigUi.getContext(); |
| switch (eapMethod) { |
| case WIFI_EAP_METHOD_PWD: |
| setPhase2Invisible(); |
| setCaCertInvisible(); |
| setMinTlsVerInvisible(); |
| setOcspInvisible(); |
| setDomainInvisible(); |
| setAnonymousIdentInvisible(); |
| setUserCertInvisible(); |
| mView.findViewById(R.id.l_sim).setVisibility(View.GONE); |
| break; |
| case WIFI_EAP_METHOD_TLS: |
| mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE); |
| setPhase2Invisible(); |
| setAnonymousIdentInvisible(); |
| setPasswordInvisible(); |
| mView.findViewById(R.id.l_sim).setVisibility(View.GONE); |
| break; |
| case WIFI_EAP_METHOD_PEAP: |
| // Reset adapter if needed |
| if (mPhase2Adapter != mPhase2PeapAdapter) { |
| mPhase2Adapter = mPhase2PeapAdapter; |
| mPhase2Spinner.setAdapter(mPhase2Adapter); |
| } |
| mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE); |
| setAnonymousIdVisible(); |
| showPeapFields(); |
| setUserCertInvisible(); |
| break; |
| case WIFI_EAP_METHOD_TTLS: |
| // Reset adapter if needed |
| if (mPhase2Adapter != mPhase2TtlsAdapter) { |
| mPhase2Adapter = mPhase2TtlsAdapter; |
| mPhase2Spinner.setAdapter(mPhase2Adapter); |
| } |
| mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE); |
| setAnonymousIdVisible(); |
| setUserCertInvisible(); |
| mView.findViewById(R.id.l_sim).setVisibility(View.GONE); |
| break; |
| case WIFI_EAP_METHOD_SIM: |
| case WIFI_EAP_METHOD_AKA: |
| case WIFI_EAP_METHOD_AKA_PRIME: |
| setPhase2Invisible(); |
| setAnonymousIdentInvisible(); |
| setCaCertInvisible(); |
| setMinTlsVerInvisible(); |
| setOcspInvisible(); |
| setDomainInvisible(); |
| setUserCertInvisible(); |
| setPasswordInvisible(); |
| setIdentityInvisible(); |
| break; |
| } |
| |
| if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { |
| String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); |
| if (eapCertSelection.equals(mUnspecifiedCertString) |
| || (mIsTrustOnFirstUseSupported |
| && eapCertSelection.equals(mTrustOnFirstUse))) { |
| setMinTlsVerInvisible(); |
| // Domain suffix matching is not relevant if the user hasn't chosen a CA |
| // certificate yet, or chooses not to validate the EAP server. |
| setDomainInvisible(); |
| // Ocsp is an additional validation step for a server certifidate. |
| // This field is not relevant if the user hasn't chosen a valid |
| // CA certificate yet. |
| setOcspInvisible(); |
| } |
| } |
| } |
| |
| private void showPeapFields() { |
| int phase2Method = mPhase2Spinner.getSelectedItemPosition(); |
| if (phase2Method == WIFI_PEAP_PHASE2_SIM || phase2Method == WIFI_PEAP_PHASE2_AKA |
| || phase2Method == WIFI_PEAP_PHASE2_AKA_PRIME) { |
| mEapIdentityView.setText(""); |
| mView.findViewById(R.id.l_identity).setVisibility(View.GONE); |
| setPasswordInvisible(); |
| mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE); |
| } else { |
| mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE); |
| mView.findViewById(R.id.l_sim).setVisibility(View.GONE); |
| } |
| } |
| |
| private void setIdentityInvisible() { |
| mView.findViewById(R.id.l_identity).setVisibility(View.GONE); |
| } |
| |
| private void setPhase2Invisible() { |
| mView.findViewById(R.id.l_phase2).setVisibility(View.GONE); |
| } |
| |
| private void setCaCertInvisible() { |
| mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); |
| setSelection(mEapCaCertSpinner, mUnspecifiedCertString); |
| } |
| |
| private void setMinTlsVerInvisible() { |
| mView.findViewById(R.id.l_min_tls_ver).setVisibility(View.GONE); |
| mEapMinTlsVerSpinner.setSelection(WifiEnterpriseConfig.TLS_V1_0); |
| } |
| |
| private void setOcspInvisible() { |
| mView.findViewById(R.id.l_ocsp).setVisibility(View.GONE); |
| mEapOcspSpinner.setSelection(WifiEnterpriseConfig.OCSP_NONE); |
| } |
| |
| private void setDomainInvisible() { |
| mView.findViewById(R.id.l_domain).setVisibility(View.GONE); |
| mEapDomainView.setText(""); |
| } |
| |
| private void setUserCertInvisible() { |
| mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE); |
| setSelection(mEapUserCertSpinner, mUnspecifiedCertString); |
| } |
| |
| private void setAnonymousIdentInvisible() { |
| mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE); |
| mEapAnonymousView.setText(""); |
| } |
| |
| @VisibleForTesting |
| void setAnonymousIdVisible() { |
| mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); |
| mEapAnonymousView.setText(DEFAULT_ANONYMOUS_ID); |
| } |
| |
| private void setPasswordInvisible() { |
| mPasswordView.setText(""); |
| mView.findViewById(R.id.password_layout).setVisibility(View.GONE); |
| mView.findViewById(R.id.show_password_layout).setVisibility(View.GONE); |
| } |
| |
| private void setEapMethodInvisible() { |
| mView.findViewById(R.id.eap).setVisibility(View.GONE); |
| } |
| |
| private void showIpConfigFields() { |
| WifiConfiguration config = null; |
| |
| mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE); |
| |
| if (mWifiEntry != null && mWifiEntry.isSaved()) { |
| config = mWifiEntry.getWifiConfiguration(); |
| } |
| |
| if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) { |
| mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE); |
| if (mIpAddressView == null) { |
| mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress); |
| mIpAddressView.addTextChangedListener(this); |
| mGatewayView = (TextView) mView.findViewById(R.id.gateway); |
| mGatewayView.addTextChangedListener(getIpConfigFieldsTextWatcher(mGatewayView)); |
| mNetworkPrefixLengthView = (TextView) mView.findViewById( |
| R.id.network_prefix_length); |
| mNetworkPrefixLengthView.addTextChangedListener( |
| getIpConfigFieldsTextWatcher(mNetworkPrefixLengthView)); |
| mDns1View = (TextView) mView.findViewById(R.id.dns1); |
| mDns1View.addTextChangedListener(getIpConfigFieldsTextWatcher(mDns1View)); |
| mDns2View = (TextView) mView.findViewById(R.id.dns2); |
| mDns2View.addTextChangedListener(this); |
| } |
| if (config != null) { |
| StaticIpConfiguration staticConfig = config.getIpConfiguration() |
| .getStaticIpConfiguration(); |
| if (staticConfig != null) { |
| if (staticConfig.getIpAddress() != null) { |
| mIpAddressView.setText( |
| staticConfig.getIpAddress().getAddress().getHostAddress()); |
| mNetworkPrefixLengthView.setText(Integer.toString( |
| staticConfig.getIpAddress().getPrefixLength())); |
| } |
| |
| if (staticConfig.getGateway() != null) { |
| mGatewayView.setText(staticConfig.getGateway().getHostAddress()); |
| } |
| |
| Iterator<InetAddress> dnsIterator = staticConfig.getDnsServers().iterator(); |
| if (dnsIterator.hasNext()) { |
| mDns1View.setText(dnsIterator.next().getHostAddress()); |
| } |
| if (dnsIterator.hasNext()) { |
| mDns2View.setText(dnsIterator.next().getHostAddress()); |
| } |
| } |
| } |
| } else { |
| mView.findViewById(R.id.staticip).setVisibility(View.GONE); |
| } |
| } |
| |
| private void showProxyFields() { |
| WifiConfiguration config = null; |
| |
| mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE); |
| |
| if (mWifiEntry != null && mWifiEntry.isSaved()) { |
| config = mWifiEntry.getWifiConfiguration(); |
| } |
| |
| if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) { |
| setVisibility(R.id.proxy_warning_limited_support, View.VISIBLE); |
| setVisibility(R.id.proxy_fields, View.VISIBLE); |
| setVisibility(R.id.proxy_pac_field, View.GONE); |
| if (mProxyHostView == null) { |
| mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname); |
| mProxyHostView.addTextChangedListener(this); |
| mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port); |
| mProxyPortView.addTextChangedListener(this); |
| mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist); |
| mProxyExclusionListView.addTextChangedListener(this); |
| } |
| if (config != null) { |
| ProxyInfo proxyProperties = config.getHttpProxy(); |
| if (proxyProperties != null) { |
| mProxyHostView.setText(proxyProperties.getHost()); |
| mProxyPortView.setText(Integer.toString(proxyProperties.getPort())); |
| mProxyExclusionListView.setText( |
| ProxyUtils.exclusionListAsString(proxyProperties.getExclusionList())); |
| } |
| } |
| } else if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_PAC) { |
| setVisibility(R.id.proxy_warning_limited_support, View.GONE); |
| setVisibility(R.id.proxy_fields, View.GONE); |
| setVisibility(R.id.proxy_pac_field, View.VISIBLE); |
| |
| if (mProxyPacView == null) { |
| mProxyPacView = (TextView) mView.findViewById(R.id.proxy_pac); |
| mProxyPacView.addTextChangedListener(this); |
| } |
| if (config != null) { |
| ProxyInfo proxyInfo = config.getHttpProxy(); |
| if (proxyInfo != null) { |
| mProxyPacView.setText(proxyInfo.getPacFileUrl().toString()); |
| } |
| } |
| } else { |
| setVisibility(R.id.proxy_warning_limited_support, View.GONE); |
| setVisibility(R.id.proxy_fields, View.GONE); |
| setVisibility(R.id.proxy_pac_field, View.GONE); |
| } |
| } |
| |
| private void setVisibility(int id, int visibility) { |
| final View v = mView.findViewById(id); |
| if (v != null) { |
| v.setVisibility(visibility); |
| } |
| } |
| |
| @VisibleForTesting |
| AndroidKeystoreAliasLoader getAndroidKeystoreAliasLoader() { |
| return new AndroidKeystoreAliasLoader(KeyProperties.NAMESPACE_WIFI); |
| } |
| |
| @VisibleForTesting |
| void loadSims() { |
| List<SubscriptionInfo> activeSubscriptionInfos = mContext |
| .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList(); |
| if (activeSubscriptionInfos == null) { |
| activeSubscriptionInfos = Collections.EMPTY_LIST; |
| } |
| mActiveSubscriptionInfos.clear(); |
| |
| // Shows disabled 'No SIM' when there is no active subscription. |
| if (activeSubscriptionInfos.isEmpty()) { |
| final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)}; |
| mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim)); |
| mEapSimSpinner.setSelection(0 /* position */); |
| mEapSimSpinner.setEnabled(false); |
| return; |
| } |
| |
| // Shows display name of each active subscription. |
| ArrayMap<Integer, CharSequence> displayNames = new ArrayMap<>(); |
| int defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); |
| for (SubscriptionInfo activeSubInfo : activeSubscriptionInfos) { |
| // If multiple SIMs have the same carrier id, only the first or default data SIM is |
| // displayed. |
| if (displayNames.containsKey(activeSubInfo.getCarrierId()) |
| && defaultDataSubscriptionId != activeSubInfo.getSubscriptionId()) { |
| continue; |
| } |
| displayNames.put(activeSubInfo.getCarrierId(), |
| SubscriptionUtil.getUniqueSubscriptionDisplayName(activeSubInfo, mContext)); |
| mActiveSubscriptionInfos.put(activeSubInfo.getCarrierId(), activeSubInfo); |
| } |
| mEapSimSpinner.setAdapter( |
| getSpinnerAdapter(displayNames.values().toArray(new String[displayNames.size()]))); |
| mEapSimSpinner.setSelection(0 /* position */); |
| if (displayNames.size() == 1) { |
| mEapSimSpinner.setEnabled(false); |
| } |
| } |
| |
| @VisibleForTesting |
| void loadCertificates( |
| Spinner spinner, |
| Collection<String> choices, |
| String noCertificateString, |
| boolean showMultipleCerts, |
| boolean showUsePreinstalledCertOption) { |
| final Context context = mConfigUi.getContext(); |
| |
| ArrayList<String> certs = new ArrayList<String>(); |
| certs.add(mUnspecifiedCertString); |
| if (showMultipleCerts) { |
| certs.add(mMultipleCertSetString); |
| } |
| if (showUsePreinstalledCertOption) { |
| certs.add(mUseSystemCertsString); |
| if (mIsTrustOnFirstUseSupported) { |
| certs.add(mTrustOnFirstUse); |
| } |
| certs.add(mInstallCertsString); |
| } |
| |
| if (choices != null && choices.size() != 0) { |
| certs.addAll(choices.stream() |
| .filter(certificateName -> { |
| for (String undesired : UNDESIRED_CERTIFICATES) { |
| if (certificateName.startsWith(undesired)) { |
| return false; |
| } |
| } |
| return true; |
| }).collect(Collectors.toList())); |
| } |
| |
| if (!TextUtils.isEmpty(noCertificateString) |
| && mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) { |
| certs.add(noCertificateString); |
| } |
| |
| // If there is only mUnspecifiedCertString and one item to select, only shows the item |
| if (certs.size() == 2) { |
| certs.remove(mUnspecifiedCertString); |
| spinner.setEnabled(false); |
| } else { |
| spinner.setEnabled(true); |
| } |
| |
| final ArrayAdapter<CharSequence> adapter = getSpinnerAdapter( |
| certs.toArray(new String[certs.size()])); |
| spinner.setAdapter(adapter); |
| } |
| |
| private void setSelection(Spinner spinner, String value) { |
| if (value != null) { |
| @SuppressWarnings("unchecked") |
| ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter(); |
| for (int i = adapter.getCount() - 1; i >= 0; --i) { |
| if (value.equals(adapter.getItem(i))) { |
| spinner.setSelection(i); |
| break; |
| } |
| } |
| } |
| } |
| |
| public int getMode() { |
| return mMode; |
| } |
| |
| @Override |
| public void afterTextChanged(Editable s) { |
| ThreadUtils.postOnMainThread(() -> { |
| showWarningMessagesIfAppropriate(); |
| enableSubmitIfAppropriate(); |
| }); |
| } |
| |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
| // work done in afterTextChanged |
| } |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| // work done in afterTextChanged |
| } |
| |
| /* TODO: Add more test cases for this TextWatcher b/186368002 |
| * This TextWatcher is for IP config fields (Gateway/Network Prefix Length/DNS1) to prevent |
| * to rewrite the value in these columns that the user wanted to change after they saved. |
| * When afterTextChanged we will check the text is empty or not then set the Hint for user. |
| */ |
| private TextWatcher getIpConfigFieldsTextWatcher(final TextView view) { |
| return new TextWatcher() { |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
| // work done in afterTextChanged |
| } |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| // work done in afterTextChanged |
| } |
| |
| @Override |
| public void afterTextChanged(Editable s) { |
| if (s.length() == 0) { |
| if (view.getId() == R.id.gateway) { |
| mGatewayView.setHint(R.string.wifi_gateway_hint); |
| } else if (view.getId() == R.id.network_prefix_length) { |
| mNetworkPrefixLengthView.setHint(R.string.wifi_network_prefix_length_hint); |
| } else if (view.getId() == R.id.dns1) { |
| mDns1View.setHint(R.string.wifi_dns1_hint); |
| } |
| Button submit = mConfigUi.getSubmitButton(); |
| if (submit == null) return; |
| |
| submit.setEnabled(false); |
| } else { |
| ThreadUtils.postOnMainThread(() -> { |
| showWarningMessagesIfAppropriate(); |
| enableSubmitIfAppropriate(); |
| }); |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { |
| if (textView == mPasswordView) { |
| if (id == EditorInfo.IME_ACTION_DONE && isSubmittable()) { |
| mConfigUi.dispatchSubmit(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { |
| if (view == mPasswordView) { |
| if (keyCode == KeyEvent.KEYCODE_ENTER && isSubmittable()) { |
| mConfigUi.dispatchSubmit(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void onCheckedChanged(CompoundButton view, boolean isChecked) { |
| if (view.getId() == R.id.show_password) { |
| int pos = mPasswordView.getSelectionEnd(); |
| mPasswordView.setInputType(InputType.TYPE_CLASS_TEXT |
| | (isChecked ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD |
| : InputType.TYPE_TEXT_VARIATION_PASSWORD)); |
| if (pos >= 0) { |
| ((EditText) mPasswordView).setSelection(pos); |
| } |
| } else if (view.getId() == R.id.wifi_advanced_togglebox) { |
| // Hide the SoftKeyboard temporary to let user can see most of the expanded items. |
| hideSoftKeyboard(mView.getWindowToken()); |
| view.setVisibility(View.GONE); |
| mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE); |
| } |
| } |
| |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
| if (parent == mSecuritySpinner) { |
| // Convert menu position to actual Wi-Fi security type |
| mWifiEntrySecurity = mSecurityInPosition[position]; |
| showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true); |
| |
| if (WifiDppUtils.isSupportEnrolleeQrCodeScanner(mContext, mWifiEntrySecurity)) { |
| mSsidScanButton.setVisibility(View.VISIBLE); |
| } else { |
| mSsidScanButton.setVisibility(View.GONE); |
| } |
| } else if (parent == mEapMethodSpinner) { |
| final int selectedItemPosition = mEapMethodSpinner.getSelectedItemPosition(); |
| if (mLastShownEapMethod != selectedItemPosition) { |
| mLastShownEapMethod = selectedItemPosition; |
| showSecurityFields(/* refreshEapMethods */false, /* refreshCertificates */ true); |
| } |
| } else if (parent == mEapCaCertSpinner) { |
| String selectedItem = parent.getItemAtPosition(position).toString(); |
| if (selectedItem.equals(mInstallCertsString)) { |
| startActivityForInstallCerts(); |
| } |
| showSecurityFields(/* refreshEapMethods */ false, /* refreshCertificates */ false); |
| } else if (parent == mPhase2Spinner |
| && mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_PEAP) { |
| showPeapFields(); |
| } else if (parent == mProxySettingsSpinner) { |
| showProxyFields(); |
| } else if (parent == mHiddenSettingsSpinner) { |
| mHiddenWarningView.setVisibility(position == NOT_HIDDEN_NETWORK |
| ? View.GONE : View.VISIBLE); |
| } else { |
| showIpConfigFields(); |
| } |
| showWarningMessagesIfAppropriate(); |
| enableSubmitIfAppropriate(); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| // |
| } |
| |
| /** |
| * Start the install page for user to install the existing certificate. |
| */ |
| private void startActivityForInstallCerts() { |
| Intent intent = new Intent(ACTION_INSTALL_CERTS); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| intent.setComponent(new ComponentName(PACKAGE_INSTALL_CERTS, CLASS_INSTALL_CERTS)); |
| intent.putExtra(KEY_INSTALL_CERTIFICATE, INSTALL_CERTIFICATE_VALUE); |
| |
| mContext.startActivity(intent); |
| } |
| |
| /** |
| * Make the characters of the password visible if show_password is checked. |
| */ |
| public void updatePassword() { |
| TextView passwdView = (TextView) mView.findViewById(R.id.password); |
| passwdView.setInputType(InputType.TYPE_CLASS_TEXT |
| | (((CheckBox) mView.findViewById(R.id.show_password)).isChecked() |
| ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD |
| : InputType.TYPE_TEXT_VARIATION_PASSWORD)); |
| } |
| |
| public WifiEntry getWifiEntry() { |
| return mWifiEntry; |
| } |
| |
| private void configureSecuritySpinner() { |
| mConfigUi.setTitle(R.string.wifi_add_network); |
| |
| mSsidView = (TextView) mView.findViewById(R.id.ssid); |
| mSsidView.addTextChangedListener(this); |
| mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security)); |
| mSecuritySpinner.setOnItemSelectedListener(this); |
| |
| ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext, |
| android.R.layout.simple_spinner_item, android.R.id.text1); |
| spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); |
| mSecuritySpinner.setAdapter(spinnerAdapter); |
| int idx = 0; |
| |
| // Populate the Wi-Fi security spinner with the various supported key management types |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_none)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_NONE; |
| if (mWifiManager.isEnhancedOpenSupported()) { |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_owe)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_OWE; |
| } |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_wep)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_WEP; |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_wpa_wpa2)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_PSK; |
| if (mWifiManager.isWpa3SaeSupported()) { |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_sae)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_SAE; |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_eap_wpa_wpa2)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_EAP; |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_eap_wpa3)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE; |
| } else { |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_eap)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_EAP; |
| } |
| if (mWifiManager.isWpa3SuiteBSupported()) { |
| spinnerAdapter.add(mContext.getString(R.string.wifi_security_eap_suiteb)); |
| mSecurityInPosition[idx++] = WifiEntry.SECURITY_EAP_SUITE_B; |
| } |
| |
| spinnerAdapter.notifyDataSetChanged(); |
| |
| mView.findViewById(R.id.type).setVisibility(View.VISIBLE); |
| |
| showIpConfigFields(); |
| showProxyFields(); |
| mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); |
| // Hidden option can be changed only when the user adds a network manually. |
| mView.findViewById(R.id.hidden_settings_field).setVisibility(View.VISIBLE); |
| ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox)) |
| .setOnCheckedChangeListener(this); |
| // Set correct accessibility strings. |
| setAdvancedOptionAccessibilityString(); |
| } |
| |
| /** |
| * For each target string in {@code targetStringArray} try to find if it appears in {@code |
| * originalStringArray}, if found then use the corresponding string, which have the same index |
| * of the target string in {@code replacementStringArray}, to replace it. And finally return the |
| * whole new string array back to caller. |
| */ |
| @VisibleForTesting |
| CharSequence[] findAndReplaceTargetStrings(CharSequence[] originalStringArray, |
| CharSequence[] targetStringArray, CharSequence[] replacementStringArray) { |
| // The length of the targetStringArray and replacementStringArray should be the same, each |
| // item in the targetStringArray should have a 1:1 mapping to replacementStringArray, so |
| // just return the original string if the lengths are different. |
| if (targetStringArray.length != replacementStringArray.length) { |
| return originalStringArray; |
| } |
| |
| final CharSequence[] returnEntries = new CharSequence[originalStringArray.length]; |
| for (int i = 0; i < originalStringArray.length; i++) { |
| returnEntries[i] = originalStringArray[i]; |
| for (int j = 0; j < targetStringArray.length; j++) { |
| if (TextUtils.equals(originalStringArray[i], targetStringArray[j])) { |
| returnEntries[i] = replacementStringArray[j]; |
| } |
| } |
| } |
| return returnEntries; |
| } |
| |
| private ArrayAdapter<CharSequence> getSpinnerAdapter( |
| int contentStringArrayResId) { |
| return getSpinnerAdapter( |
| mContext.getResources().getStringArray(contentStringArrayResId)); |
| } |
| |
| @VisibleForTesting |
| ArrayAdapter<CharSequence> getSpinnerAdapter( |
| String[] contentStringArray) { |
| ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext, |
| android.R.layout.simple_spinner_item, contentStringArray); |
| spinnerAdapter.setDropDownViewResource( |
| android.R.layout.simple_spinner_dropdown_item); |
| return spinnerAdapter; |
| } |
| |
| /** |
| * This function is to span the TTS strings to each EAP method items in the |
| * spinner to have detail TTS content for the TTS engine usage. |
| */ |
| private ArrayAdapter<CharSequence> getSpinnerAdapterWithEapMethodsTts( |
| int contentStringArrayResId) { |
| final Resources res = mContext.getResources(); |
| CharSequence[] sourceStrings = res.getStringArray( |
| contentStringArrayResId); |
| CharSequence[] targetStrings = res.getStringArray( |
| R.array.wifi_eap_method_target_strings); |
| CharSequence[] ttsStrings = res.getStringArray( |
| R.array.wifi_eap_method_tts_strings); |
| |
| // Replace the target strings with tts strings and save all in a new array. |
| final CharSequence[] newTtsSourceStrings = findAndReplaceTargetStrings( |
| sourceStrings, targetStrings, ttsStrings); |
| |
| // Build new TtsSpan text arrays for TalkBack. |
| final CharSequence[] accessibilityArray = createAccessibleEntries( |
| sourceStrings, newTtsSourceStrings); |
| |
| // Return a new ArrayAdapter with the new TalkBack array. |
| ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>( |
| mContext, android.R.layout.simple_spinner_item, accessibilityArray); |
| spinnerAdapter.setDropDownViewResource( |
| android.R.layout.simple_spinner_dropdown_item); |
| return spinnerAdapter; |
| } |
| |
| private SpannableString[] createAccessibleEntries(CharSequence[] entries, |
| CharSequence[] contentDescriptions) { |
| final SpannableString[] accessibleEntries = new SpannableString[entries.length]; |
| for (int i = 0; i < entries.length; i++) { |
| accessibleEntries[i] = com.android.settings.Utils.createAccessibleSequence(entries[i], |
| contentDescriptions[i].toString()); |
| } |
| return accessibleEntries; |
| } |
| |
| private void hideSoftKeyboard(IBinder windowToken) { |
| final InputMethodManager inputMethodManager = mContext.getSystemService( |
| InputMethodManager.class); |
| inputMethodManager.hideSoftInputFromWindow(windowToken, 0 /* flags */); |
| } |
| |
| private void setAdvancedOptionAccessibilityString() { |
| final CheckBox advancedToggleBox = mView.findViewById(R.id.wifi_advanced_togglebox); |
| advancedToggleBox.setAccessibilityDelegate(new AccessibilityDelegate() { |
| @Override |
| public void onInitializeAccessibilityNodeInfo( |
| View v, AccessibilityNodeInfo info) { |
| super.onInitializeAccessibilityNodeInfo(v, info); |
| // To let TalkBack don't pronounce checked/unchecked. |
| info.setCheckable(false /* checkable */); |
| // To let TalkBack don't pronounce CheckBox. |
| info.setClassName(null /* className */); |
| // Customize TalkBack's pronunciation which been appended to "Double-tap to". |
| final AccessibilityAction customClick = new AccessibilityAction( |
| AccessibilityNodeInfo.ACTION_CLICK, |
| mContext.getString(R.string.wifi_advanced_toggle_description_collapsed)); |
| info.addAction(customClick); |
| } |
| }); |
| } |
| |
| @VisibleForTesting |
| Spinner getEapMinTlsVerSpinner(boolean isTlsV13Supported) { |
| Spinner spinner = mView.findViewById(R.id.min_tls_ver); |
| String[] stringArray = mContext.getResources().getStringArray(R.array.wifi_eap_tls_ver); |
| if (!isTlsV13Supported) { |
| Log.w(TAG, "Wi-Fi Enterprise TLS v1.3 is not supported on this device"); |
| List<String> list = new ArrayList<>(Arrays.asList(stringArray)); |
| list.remove(WifiEnterpriseConfig.TLS_V1_3); |
| stringArray = list.toArray(new String[0]); |
| } |
| spinner.setAdapter(getSpinnerAdapter(stringArray)); |
| return spinner; |
| } |
| } |