/* * Copyright (C) 2010 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.Context; import android.content.res.Resources; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; import android.net.LinkAddress; import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.net.ProxyInfo; import android.net.StaticIpConfiguration; import android.net.Uri; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.AuthAlgorithm; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiEnterpriseConfig.Eap; import android.net.wifi.WifiEnterpriseConfig.Phase2; import android.net.wifi.WifiInfo; import android.os.Handler; import android.os.UserManager; import android.security.Credentials; import android.security.KeyStore; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; 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.LinearLayout; import android.widget.Spinner; import android.widget.TextView; import com.android.settings.ProxySelector; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.wifi.AccessPoint; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; /** * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to * share the logic for controlling buttons, text fields, etc. */ public class WifiConfigController implements TextWatcher, AdapterView.OnItemSelectedListener, OnCheckedChangeListener, TextView.OnEditorActionListener, View.OnKeyListener{ private static final String TAG = "WifiConfigController"; private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; private final WifiConfigUiBase mConfigUi; private final View mView; private final AccessPoint mAccessPoint; /* This value comes from "wifi_ip_settings" resource array */ private static final int DHCP = 0; private static final int STATIC_IP = 1; /* 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_NONE = 0; public static final int WIFI_PEAP_PHASE2_MSCHAPV2 = 1; public static final int WIFI_PEAP_PHASE2_GTC = 2; /* Phase2 methods supported by PEAP are limited */ private final ArrayAdapter<String> mPhase2PeapAdapter; /* Full list of phase2 methods */ private final ArrayAdapter<String> mPhase2FullAdapter; private final Handler mTextViewChangedHandler; // e.g. AccessPoint.SECURITY_NONE private int mAccessPointSecurity; private TextView mPasswordView; private String mUnspecifiedCertString; private String mMultipleCertSetString; private String mUseSystemCertsString; private String mDoNotProvideEapUserCertString; private String mDoNotValidateEapServerString; private Spinner mSecuritySpinner; private Spinner mEapMethodSpinner; private Spinner mEapCaCertSpinner; private TextView mEapDomainView; private Spinner mPhase2Spinner; // Associated with mPhase2Spinner, one of mPhase2FullAdapter or mPhase2PeapAdapter private ArrayAdapter<String> mPhase2Adapter; private Spinner mEapUserCertSpinner; private TextView mEapIdentityView; private TextView mEapAnonymousView; private Spinner mIpSettingsSpinner; private TextView mIpAddressView; private TextView mGatewayView; private TextView mNetworkPrefixLengthView; private TextView mDns1View; private TextView mDns2View; private Spinner mProxySettingsSpinner; 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; public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode) { mConfigUi = parent; mView = view; mAccessPoint = accessPoint; mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE : accessPoint.getSecurity(); mMode = mode; mTextViewChangedHandler = new Handler(); mContext = mConfigUi.getContext(); final Resources res = mContext.getResources(); mLevels = res.getStringArray(R.array.wifi_signal); mPhase2PeapAdapter = new ArrayAdapter<String>( mContext, android.R.layout.simple_spinner_item, res.getStringArray(R.array.wifi_peap_phase2_entries)); mPhase2PeapAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mPhase2FullAdapter = new ArrayAdapter<String>( mContext, android.R.layout.simple_spinner_item, res.getStringArray(R.array.wifi_phase2_entries)); mPhase2FullAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 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); mDoNotProvideEapUserCertString = mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); mDoNotValidateEapServerString = mContext.getString(R.string.wifi_do_not_validate_eap_server); 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); if (mAccessPoint == null) { // new network 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); mView.findViewById(R.id.type).setVisibility(View.VISIBLE); showIpConfigFields(); showProxyFields(); mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox)) .setOnCheckedChangeListener(this); mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); } else { mConfigUi.setTitle(mAccessPoint.getSsid()); ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); boolean showAdvancedFields = false; if (mAccessPoint.isSaved()) { WifiConfiguration config = mAccessPoint.getConfig(); if (config.getIpAssignment() == IpAssignment.STATIC) { mIpSettingsSpinner.setSelection(STATIC_IP); showAdvancedFields = true; // Display IP address. StaticIpConfiguration staticConfig = config.getStaticIpConfiguration(); if (staticConfig != null && staticConfig.ipAddress != null) { addRow(group, R.string.wifi_ip_address, staticConfig.ipAddress.getAddress().getHostAddress()); } } else { mIpSettingsSpinner.setSelection(DHCP); } mSharedCheckBox.setEnabled(config.shared); if (!config.shared) { showAdvancedFields = true; } if (config.getProxySettings() == ProxySettings.STATIC) { mProxySettingsSpinner.setSelection(PROXY_STATIC); showAdvancedFields = true; } else if (config.getProxySettings() == 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 ((!mAccessPoint.isSaved() && !mAccessPoint.isActive()) || mMode != WifiConfigUiBase.MODE_VIEW) { showSecurityFields(); showIpConfigFields(); showProxyFields(); final CheckBox advancedTogglebox = (CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox); mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE); advancedTogglebox.setOnCheckedChangeListener(this); advancedTogglebox.setChecked(showAdvancedFields); mView.findViewById(R.id.wifi_advanced_fields) .setVisibility(showAdvancedFields ? View.VISIBLE : View.GONE); } if (mMode == WifiConfigUiBase.MODE_MODIFY) { mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); } else if (mMode == WifiConfigUiBase.MODE_CONNECT) { mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); } else { final DetailedState state = mAccessPoint.getDetailedState(); final String signalLevel = getSignalString(); if ((state == null || state == DetailedState.DISCONNECTED) && signalLevel != null) { mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect)); } else { if (state != null) { boolean isEphemeral = mAccessPoint.isEphemeral(); WifiConfiguration config = mAccessPoint.getConfig(); String providerFriendlyName = null; if (config != null && config.isPasspoint()) { providerFriendlyName = config.providerFriendlyName; } String summary = AccessPoint.getSummary( mConfigUi.getContext(), state, isEphemeral, providerFriendlyName); addRow(group, R.string.wifi_status, summary); } if (signalLevel != null) { addRow(group, R.string.wifi_signal, signalLevel); } WifiInfo info = mAccessPoint.getInfo(); if (info != null && info.getLinkSpeed() != -1) { addRow(group, R.string.wifi_speed, String.format( res.getString(R.string.link_speed), info.getLinkSpeed())); } if (info != null && info.getFrequency() != -1) { final int frequency = info.getFrequency(); String band = null; if (frequency >= AccessPoint.LOWER_FREQ_24GHZ && frequency < AccessPoint.HIGHER_FREQ_24GHZ) { band = res.getString(R.string.wifi_band_24ghz); } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ && frequency < AccessPoint.HIGHER_FREQ_5GHZ) { band = res.getString(R.string.wifi_band_5ghz); } else { Log.e(TAG, "Unexpected frequency " + frequency); } if (band != null) { addRow(group, R.string.wifi_frequency, band); } } addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false)); mView.findViewById(R.id.ip_fields).setVisibility(View.GONE); } if (mAccessPoint.isSaved() || mAccessPoint.isActive()) { mConfigUi.setForgetButton(res.getString(R.string.wifi_forget)); } } } final UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); if (!userManager.isSplitSystemUser()) { mSharedCheckBox.setVisibility(View.GONE); } mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel)); if (mConfigUi.getSubmitButton() != null) { enableSubmitIfAppropriate(); } } 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); } private String getSignalString() { final int level = mAccessPoint.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 isSubmittable() { boolean enabled = false; boolean passwordInvalid = false; if (mPasswordView != null && ((mAccessPointSecurity == AccessPoint.SECURITY_WEP && mPasswordView.length() == 0) || (mAccessPointSecurity == AccessPoint.SECURITY_PSK && mPasswordView.length() < 8))) { passwordInvalid = true; } if ((mSsidView != null && mSsidView.length() == 0) || ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid)) { enabled = false; } else { enabled = ipAndProxyFieldsAreValid(); } if (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; } if (caCertSelection.equals(mUseSystemCertsString) && mEapDomainView != null && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE && TextUtils.isEmpty(mEapDomainView.getText().toString())) { // Disallow submit if the user chooses to use system certificates for EAP server // validation, but does not provide a domain. enabled = false; } } if (mEapUserCertSpinner != null && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE && ((String) 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_ca_cert_warning).setVisibility(View.GONE); mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE); if (mEapCaCertSpinner != null && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); if (caCertSelection.equals(mDoNotValidateEapServerString)) { // Display warning if user chooses not to validate the EAP server with a // user-supplied CA certificate in an EAP network configuration. mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE); } if (caCertSelection.equals(mUseSystemCertsString) && mEapDomainView != null && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE && TextUtils.isEmpty(mEapDomainView.getText().toString())) { // Display warning if user chooses to use pre-installed public CA certificates // without restricting the server domain that these certificates can be used to // validate. mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE); } } } /* package */ WifiConfiguration getConfig() { if (mMode == WifiConfigUiBase.MODE_VIEW) { return null; } WifiConfiguration config = new WifiConfiguration(); if (mAccessPoint == null) { config.SSID = AccessPoint.convertToQuotedString( mSsidView.getText().toString()); // If the user adds a network manually, assume that it is hidden. config.hiddenSSID = true; } else if (!mAccessPoint.isSaved()) { config.SSID = AccessPoint.convertToQuotedString( mAccessPoint.getSsidStr()); } else { config.networkId = mAccessPoint.getConfig().networkId; } config.shared = mSharedCheckBox.isChecked(); switch (mAccessPointSecurity) { case AccessPoint.SECURITY_NONE: config.allowedKeyManagement.set(KeyMgmt.NONE); break; case AccessPoint.SECURITY_WEP: config.allowedKeyManagement.set(KeyMgmt.NONE); config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); 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 AccessPoint.SECURITY_PSK: config.allowedKeyManagement.set(KeyMgmt.WPA_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 AccessPoint.SECURITY_EAP: config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 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_NONE: config.enterpriseConfig.setPhase2Method(Phase2.NONE); break; case WIFI_PEAP_PHASE2_MSCHAPV2: config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); break; case WIFI_PEAP_PHASE2_GTC: config.enterpriseConfig.setPhase2Method(Phase2.GTC); break; default: Log.e(TAG, "Unknown phase2 method" + phase2Method); break; } break; default: // The default index from mPhase2FullAdapter maps to the API config.enterpriseConfig.setPhase2Method(phase2Method); break; } String caCert = (String) mEapCaCertSpinner.getSelectedItem(); config.enterpriseConfig.setCaCertificateAliases(null); config.enterpriseConfig.setCaPath(null); config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString()); if (caCert.equals(mUnspecifiedCertString) || caCert.equals(mDoNotValidateEapServerString)) { // ca_cert already set to null, so do nothing. } else if (caCert.equals(mUseSystemCertsString)) { config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH); } else if (caCert.equals(mMultipleCertSetString)) { if (mAccessPoint != null) { if (!mAccessPoint.isSaved()) { Log.e(TAG, "Multiple certs can only be set " + "when editing saved network"); } config.enterpriseConfig.setCaCertificateAliases( mAccessPoint .getConfig() .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 (" + config.enterpriseConfig.getCaCertificateAliases() + ") and ca_path (" + config.enterpriseConfig.getCaPath() + ") should not both be non-null"); } 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; default: return null; } config.setIpConfiguration( new IpConfiguration(mIpAssignment, mProxySettings, mStaticIpConfiguration, mHttpProxy)); 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 = new ProxyInfo(host, port, exclusionList); } 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 = new ProxyInfo(uri); } return true; } private Inet4Address getIPv4Address(String text) { try { return (Inet4Address) NetworkUtils.numericToInetAddress(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; } 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; } staticIpConfiguration.ipAddress = 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 = NetworkUtils.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; } staticIpConfiguration.gateway = gatewayAddr; } String dns = mDns1View.getText().toString(); InetAddress dnsAddr = null; 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; } staticIpConfiguration.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; } staticIpConfiguration.dnsServers.add(dnsAddr); } return 0; } private void showSecurityFields() { if (mAccessPointSecurity == AccessPoint.SECURITY_NONE) { 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 (mAccessPoint != null && mAccessPoint.isSaved()) { mPasswordView.setHint(R.string.wifi_unchanged); } } if (mAccessPointSecurity != AccessPoint.SECURITY_EAP) { mView.findViewById(R.id.eap).setVisibility(View.GONE); return; } mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); if (mEapMethodSpinner == null) { mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method); mEapMethodSpinner.setOnItemSelectedListener(this); if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean( com.android.internal.R.bool.config_eap_sim_based_auth_supported)) { String[] eapMethods = mContext.getResources().getStringArray( R.array.eap_method_without_sim_auth); ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, eapMethods); spinnerAdapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); mEapMethodSpinner.setAdapter(spinnerAdapter); } mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2); mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert); mEapCaCertSpinner.setOnItemSelectedListener(this); 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); loadCertificates( mEapCaCertSpinner, Credentials.CA_CERTIFICATE, mDoNotValidateEapServerString, false, true); loadCertificates( mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY, mDoNotProvideEapUserCertString, false, false); // Modifying an existing network if (mAccessPoint != null && mAccessPoint.isSaved()) { WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig; int eapMethod = enterpriseConfig.getEapMethod(); int phase2Method = enterpriseConfig.getPhase2Method(); mEapMethodSpinner.setSelection(eapMethod); showEapFieldsByMethod(eapMethod); switch (eapMethod) { case Eap.PEAP: switch (phase2Method) { case Phase2.NONE: mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE); break; case Phase2.MSCHAPV2: mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2); break; case Phase2.GTC: mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC); break; default: Log.e(TAG, "Invalid phase 2 method " + phase2Method); break; } break; default: mPhase2Spinner.setSelection(phase2Method); break; } if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) { setSelection(mEapCaCertSpinner, mUseSystemCertsString); } else { String[] caCerts = enterpriseConfig.getCaCertificateAliases(); if (caCerts == null) { setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString); } else if (caCerts.length == 1) { setSelection(mEapCaCertSpinner, caCerts[0]); } else { // Reload the cert spinner with an extra "multiple certificates added" item. loadCertificates( mEapCaCertSpinner, Credentials.CA_CERTIFICATE, mDoNotValidateEapServerString, true, true); setSelection(mEapCaCertSpinner, mMultipleCertSetString); } } 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()); } } else { showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition()); } } /** * EAP-PWD valid fields include * identity * password * EAP-PEAP valid fields include * phase2: MSCHAPV2, GTC * ca_cert * identity * anonymous_identity * password * 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); mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE); mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE); Context context = mConfigUi.getContext(); switch (eapMethod) { case WIFI_EAP_METHOD_PWD: setPhase2Invisible(); setCaCertInvisible(); setDomainInvisible(); setAnonymousIdentInvisible(); setUserCertInvisible(); break; case WIFI_EAP_METHOD_TLS: mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE); setPhase2Invisible(); setAnonymousIdentInvisible(); setPasswordInvisible(); 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); mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); setUserCertInvisible(); break; case WIFI_EAP_METHOD_TTLS: // Reset adapter if needed if (mPhase2Adapter != mPhase2FullAdapter) { mPhase2Adapter = mPhase2FullAdapter; mPhase2Spinner.setAdapter(mPhase2Adapter); } mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE); mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE); setUserCertInvisible(); break; case WIFI_EAP_METHOD_SIM: case WIFI_EAP_METHOD_AKA: case WIFI_EAP_METHOD_AKA_PRIME: setPhase2Invisible(); setAnonymousIdentInvisible(); setCaCertInvisible(); setDomainInvisible(); setUserCertInvisible(); setPasswordInvisible(); setIdentityInvisible(); break; } if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) { String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem(); if (eapCertSelection.equals(mDoNotValidateEapServerString) || eapCertSelection.equals(mUnspecifiedCertString)) { // 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(); } } } private void setIdentityInvisible() { mView.findViewById(R.id.l_identity).setVisibility(View.GONE); mPhase2Spinner.setSelection(Phase2.NONE); } private void setPhase2Invisible() { mView.findViewById(R.id.l_phase2).setVisibility(View.GONE); mPhase2Spinner.setSelection(Phase2.NONE); } private void setCaCertInvisible() { mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); setSelection(mEapCaCertSpinner, mUnspecifiedCertString); } 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(""); } 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 showIpConfigFields() { WifiConfiguration config = null; mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE); if (mAccessPoint != null && mAccessPoint.isSaved()) { config = mAccessPoint.getConfig(); } 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(this); mNetworkPrefixLengthView = (TextView) mView.findViewById( R.id.network_prefix_length); mNetworkPrefixLengthView.addTextChangedListener(this); mDns1View = (TextView) mView.findViewById(R.id.dns1); mDns1View.addTextChangedListener(this); mDns2View = (TextView) mView.findViewById(R.id.dns2); mDns2View.addTextChangedListener(this); } if (config != null) { StaticIpConfiguration staticConfig = config.getStaticIpConfiguration(); if (staticConfig != null) { if (staticConfig.ipAddress != null) { mIpAddressView.setText( staticConfig.ipAddress.getAddress().getHostAddress()); mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress .getNetworkPrefixLength())); } if (staticConfig.gateway != null) { mGatewayView.setText(staticConfig.gateway.getHostAddress()); } Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.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 (mAccessPoint != null && mAccessPoint.isSaved()) { config = mAccessPoint.getConfig(); } 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(proxyProperties.getExclusionListAsString()); } } } 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); } } private void loadCertificates( Spinner spinner, String prefix, 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); } certs.addAll( Arrays.asList(KeyStore.getInstance().list(prefix, android.os.Process.WIFI_UID))); certs.add(noCertificateString); final ArrayAdapter<String> adapter = new ArrayAdapter<String>( context, android.R.layout.simple_spinner_item, certs.toArray(new String[certs.size()])); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 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) { mTextViewChangedHandler.post(new Runnable() { public void run() { 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 } @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) { final View advancedToggle = mView.findViewById(R.id.wifi_advanced_toggle); final int toggleVisibility; final int stringID; if (isChecked) { toggleVisibility = View.VISIBLE; stringID = R.string.wifi_advanced_toggle_description_expanded; } else { toggleVisibility = View.GONE; stringID = R.string.wifi_advanced_toggle_description_collapsed; } mView.findViewById(R.id.wifi_advanced_fields).setVisibility(toggleVisibility); advancedToggle.setContentDescription(mContext.getString(stringID)); } } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (parent == mSecuritySpinner) { mAccessPointSecurity = position; showSecurityFields(); } else if (parent == mEapMethodSpinner || parent == mEapCaCertSpinner) { showSecurityFields(); } else if (parent == mProxySettingsSpinner) { showProxyFields(); } else { showIpConfigFields(); } showWarningMessagesIfAppropriate(); enableSubmitIfAppropriate(); } @Override public void onNothingSelected(AdapterView<?> parent) { // } /** * 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 AccessPoint getAccessPoint() { return mAccessPoint; } }