1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.wifi;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.net.IpConfiguration;
22import android.net.IpConfiguration.IpAssignment;
23import android.net.IpConfiguration.ProxySettings;
24import android.net.LinkAddress;
25import android.net.NetworkInfo.DetailedState;
26import android.net.NetworkUtils;
27import android.net.ProxyInfo;
28import android.net.StaticIpConfiguration;
29import android.net.Uri;
30import android.net.wifi.WifiConfiguration;
31import android.net.wifi.WifiConfiguration.AuthAlgorithm;
32import android.net.wifi.WifiConfiguration.KeyMgmt;
33import android.net.wifi.WifiEnterpriseConfig;
34import android.net.wifi.WifiEnterpriseConfig.Eap;
35import android.net.wifi.WifiEnterpriseConfig.Phase2;
36import android.net.wifi.WifiInfo;
37import android.os.Handler;
38import android.os.UserManager;
39import android.security.Credentials;
40import android.security.KeyStore;
41import android.text.Editable;
42import android.text.InputType;
43import android.text.TextUtils;
44import android.text.TextWatcher;
45import android.util.Log;
46import android.view.KeyEvent;
47import android.view.View;
48import android.view.ViewGroup;
49import android.view.inputmethod.EditorInfo;
50import android.widget.AdapterView;
51import android.widget.ArrayAdapter;
52import android.widget.Button;
53import android.widget.CheckBox;
54import android.widget.CompoundButton;
55import android.widget.CompoundButton.OnCheckedChangeListener;
56import android.widget.EditText;
57import android.widget.Spinner;
58import android.widget.TextView;
59
60import com.android.settings.ProxySelector;
61import com.android.settings.R;
62import com.android.settings.Utils;
63import com.android.settingslib.wifi.AccessPoint;
64
65import java.net.Inet4Address;
66import java.net.InetAddress;
67import java.util.ArrayList;
68import java.util.Arrays;
69import java.util.Iterator;
70
71/**
72 * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to
73 * share the logic for controlling buttons, text fields, etc.
74 */
75public class WifiConfigController implements TextWatcher,
76        AdapterView.OnItemSelectedListener, OnCheckedChangeListener,
77        TextView.OnEditorActionListener, View.OnKeyListener{
78    private static final String TAG = "WifiConfigController";
79
80    private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts";
81
82    private final WifiConfigUiBase mConfigUi;
83    private final View mView;
84    private final AccessPoint mAccessPoint;
85
86    /* This value comes from "wifi_ip_settings" resource array */
87    private static final int DHCP = 0;
88    private static final int STATIC_IP = 1;
89
90    /* These values come from "wifi_proxy_settings" resource array */
91    public static final int PROXY_NONE = 0;
92    public static final int PROXY_STATIC = 1;
93    public static final int PROXY_PAC = 2;
94
95    /* These values come from "wifi_eap_method" resource array */
96    public static final int WIFI_EAP_METHOD_PEAP = 0;
97    public static final int WIFI_EAP_METHOD_TLS  = 1;
98    public static final int WIFI_EAP_METHOD_TTLS = 2;
99    public static final int WIFI_EAP_METHOD_PWD  = 3;
100    public static final int WIFI_EAP_METHOD_SIM  = 4;
101    public static final int WIFI_EAP_METHOD_AKA  = 5;
102    public static final int WIFI_EAP_METHOD_AKA_PRIME  = 6;
103
104    /* These values come from "wifi_peap_phase2_entries" resource array */
105    public static final int WIFI_PEAP_PHASE2_NONE       = 0;
106    public static final int WIFI_PEAP_PHASE2_MSCHAPV2   = 1;
107    public static final int WIFI_PEAP_PHASE2_GTC        = 2;
108
109    /* Phase2 methods supported by PEAP are limited */
110    private final ArrayAdapter<String> mPhase2PeapAdapter;
111    /* Full list of phase2 methods */
112    private final ArrayAdapter<String> mPhase2FullAdapter;
113
114    private final Handler mTextViewChangedHandler;
115
116    // e.g. AccessPoint.SECURITY_NONE
117    private int mAccessPointSecurity;
118    private TextView mPasswordView;
119
120    private String mUnspecifiedCertString;
121    private String mMultipleCertSetString;
122    private String mUseSystemCertsString;
123    private String mDoNotProvideEapUserCertString;
124    private String mDoNotValidateEapServerString;
125
126    private Spinner mSecuritySpinner;
127    private Spinner mEapMethodSpinner;
128    private Spinner mEapCaCertSpinner;
129    private TextView mEapDomainView;
130    private Spinner mPhase2Spinner;
131    // Associated with mPhase2Spinner, one of mPhase2FullAdapter or mPhase2PeapAdapter
132    private ArrayAdapter<String> mPhase2Adapter;
133    private Spinner mEapUserCertSpinner;
134    private TextView mEapIdentityView;
135    private TextView mEapAnonymousView;
136
137    private Spinner mIpSettingsSpinner;
138    private TextView mIpAddressView;
139    private TextView mGatewayView;
140    private TextView mNetworkPrefixLengthView;
141    private TextView mDns1View;
142    private TextView mDns2View;
143
144    private Spinner mProxySettingsSpinner;
145    private TextView mProxyHostView;
146    private TextView mProxyPortView;
147    private TextView mProxyExclusionListView;
148    private TextView mProxyPacView;
149
150    private CheckBox mSharedCheckBox;
151
152    private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED;
153    private ProxySettings mProxySettings = ProxySettings.UNASSIGNED;
154    private ProxyInfo mHttpProxy = null;
155    private StaticIpConfiguration mStaticIpConfiguration = null;
156
157    private String[] mLevels;
158    private int mMode;
159    private TextView mSsidView;
160
161    private Context mContext;
162
163    public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
164            int mode) {
165        mConfigUi = parent;
166
167        mView = view;
168        mAccessPoint = accessPoint;
169        mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE :
170                accessPoint.getSecurity();
171        mMode = mode;
172
173        mTextViewChangedHandler = new Handler();
174        mContext = mConfigUi.getContext();
175        final Resources res = mContext.getResources();
176
177        mLevels = res.getStringArray(R.array.wifi_signal);
178        mPhase2PeapAdapter = new ArrayAdapter<String>(
179            mContext, android.R.layout.simple_spinner_item,
180            res.getStringArray(R.array.wifi_peap_phase2_entries));
181        mPhase2PeapAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
182
183        mPhase2FullAdapter = new ArrayAdapter<String>(
184                mContext, android.R.layout.simple_spinner_item,
185                res.getStringArray(R.array.wifi_phase2_entries));
186        mPhase2FullAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
187
188        mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified);
189        mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added);
190        mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
191        mDoNotProvideEapUserCertString =
192            mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
193        mDoNotValidateEapServerString =
194            mContext.getString(R.string.wifi_do_not_validate_eap_server);
195
196        mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
197        mIpSettingsSpinner.setOnItemSelectedListener(this);
198        mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
199        mProxySettingsSpinner.setOnItemSelectedListener(this);
200        mSharedCheckBox = (CheckBox) mView.findViewById(R.id.shared);
201
202        if (mAccessPoint == null) { // new network
203            mConfigUi.setTitle(R.string.wifi_add_network);
204
205            mSsidView = (TextView) mView.findViewById(R.id.ssid);
206            mSsidView.addTextChangedListener(this);
207            mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security));
208            mSecuritySpinner.setOnItemSelectedListener(this);
209            mView.findViewById(R.id.type).setVisibility(View.VISIBLE);
210
211            showIpConfigFields();
212            showProxyFields();
213            mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
214            ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox))
215                    .setOnCheckedChangeListener(this);
216
217            mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
218        } else {
219            mConfigUi.setTitle(mAccessPoint.getSsid());
220
221            ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
222
223            boolean showAdvancedFields = false;
224            if (mAccessPoint.isSaved()) {
225                WifiConfiguration config = mAccessPoint.getConfig();
226                if (config.getIpAssignment() == IpAssignment.STATIC) {
227                    mIpSettingsSpinner.setSelection(STATIC_IP);
228                    showAdvancedFields = true;
229                    // Display IP address.
230                    StaticIpConfiguration staticConfig = config.getStaticIpConfiguration();
231                    if (staticConfig != null && staticConfig.ipAddress != null) {
232                        addRow(group, R.string.wifi_ip_address,
233                                staticConfig.ipAddress.getAddress().getHostAddress());
234                    }
235                } else {
236                    mIpSettingsSpinner.setSelection(DHCP);
237                }
238
239                mSharedCheckBox.setEnabled(config.shared);
240                if (!config.shared) {
241                    showAdvancedFields = true;
242                }
243
244                if (config.getProxySettings() == ProxySettings.STATIC) {
245                    mProxySettingsSpinner.setSelection(PROXY_STATIC);
246                    showAdvancedFields = true;
247                } else if (config.getProxySettings() == ProxySettings.PAC) {
248                    mProxySettingsSpinner.setSelection(PROXY_PAC);
249                    showAdvancedFields = true;
250                } else {
251                    mProxySettingsSpinner.setSelection(PROXY_NONE);
252                }
253                if (config != null && config.isPasspoint()) {
254                    addRow(group, R.string.passpoint_label,
255                            String.format(mContext.getString(R.string.passpoint_content),
256                            config.providerFriendlyName));
257                }
258            }
259
260            if ((!mAccessPoint.isSaved() && !mAccessPoint.isActive())
261                    || mMode != WifiConfigUiBase.MODE_VIEW) {
262                showSecurityFields();
263                showIpConfigFields();
264                showProxyFields();
265                mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
266                ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox))
267                        .setOnCheckedChangeListener(this);
268                if (showAdvancedFields) {
269                    ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox)).setChecked(true);
270                    mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
271                }
272            }
273
274            if (mMode == WifiConfigUiBase.MODE_MODIFY) {
275                mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
276            } else if (mMode == WifiConfigUiBase.MODE_CONNECT) {
277                mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect));
278            } else {
279                final DetailedState state = mAccessPoint.getDetailedState();
280                final String signalLevel = getSignalString();
281
282                if (state == null && signalLevel != null) {
283                    mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect));
284                } else {
285                    if (state != null) {
286                        boolean isEphemeral = mAccessPoint.isEphemeral();
287                        WifiConfiguration config = mAccessPoint.getConfig();
288                        String providerFriendlyName = null;
289                        if (config != null && config.isPasspoint()) {
290                            providerFriendlyName = config.providerFriendlyName;
291                        }
292                        String summary = AccessPoint.getSummary(
293                                mConfigUi.getContext(), state, isEphemeral, providerFriendlyName);
294                        addRow(group, R.string.wifi_status, summary);
295                    }
296
297                    if (signalLevel != null) {
298                        addRow(group, R.string.wifi_signal, signalLevel);
299                    }
300
301                    WifiInfo info = mAccessPoint.getInfo();
302                    if (info != null && info.getLinkSpeed() != -1) {
303                        addRow(group, R.string.wifi_speed, String.format(
304                                res.getString(R.string.link_speed), info.getLinkSpeed()));
305                    }
306
307                    if (info != null && info.getFrequency() != -1) {
308                        final int frequency = info.getFrequency();
309                        String band = null;
310
311                        if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
312                                && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
313                            band = res.getString(R.string.wifi_band_24ghz);
314                        } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
315                                && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
316                            band = res.getString(R.string.wifi_band_5ghz);
317                        } else {
318                            Log.e(TAG, "Unexpected frequency " + frequency);
319                        }
320                        if (band != null) {
321                            addRow(group, R.string.wifi_frequency, band);
322                        }
323                    }
324
325                    addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false));
326                    mView.findViewById(R.id.ip_fields).setVisibility(View.GONE);
327                }
328                if (mAccessPoint.isSaved() || mAccessPoint.isActive()) {
329                    mConfigUi.setForgetButton(res.getString(R.string.wifi_forget));
330                }
331            }
332        }
333
334        final UserManager userManager =
335                (UserManager) mContext.getSystemService(Context.USER_SERVICE);
336        if (!userManager.isSplitSystemUser()) {
337            mSharedCheckBox.setVisibility(View.GONE);
338        }
339
340        mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel));
341        if (mConfigUi.getSubmitButton() != null) {
342            enableSubmitIfAppropriate();
343        }
344    }
345
346    private void addRow(ViewGroup group, int name, String value) {
347        View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false);
348        ((TextView) row.findViewById(R.id.name)).setText(name);
349        ((TextView) row.findViewById(R.id.value)).setText(value);
350        group.addView(row);
351    }
352
353    private String getSignalString() {
354        final int level = mAccessPoint.getLevel();
355
356        return (level > -1 && level < mLevels.length) ? mLevels[level] : null;
357    }
358
359    void hideForgetButton() {
360        Button forget = mConfigUi.getForgetButton();
361        if (forget == null) return;
362
363        forget.setVisibility(View.GONE);
364    }
365
366    void hideSubmitButton() {
367        Button submit = mConfigUi.getSubmitButton();
368        if (submit == null) return;
369
370        submit.setVisibility(View.GONE);
371    }
372
373    /* show submit button if password, ip and proxy settings are valid */
374    void enableSubmitIfAppropriate() {
375        Button submit = mConfigUi.getSubmitButton();
376        if (submit == null) return;
377
378        submit.setEnabled(isSubmittable());
379    }
380
381    boolean isSubmittable() {
382        boolean enabled = false;
383        boolean passwordInvalid = false;
384
385        if (mPasswordView != null
386                && ((mAccessPointSecurity == AccessPoint.SECURITY_WEP
387                        && mPasswordView.length() == 0)
388                    || (mAccessPointSecurity == AccessPoint.SECURITY_PSK
389                           && mPasswordView.length() < 8))) {
390            passwordInvalid = true;
391        }
392
393        if ((mSsidView != null && mSsidView.length() == 0)
394                || ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid)) {
395            enabled = false;
396        } else {
397            enabled = ipAndProxyFieldsAreValid();
398        }
399        if (mEapCaCertSpinner != null
400                && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
401            String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
402            if (caCertSelection.equals(mUnspecifiedCertString)) {
403                // Disallow submit if the user has not selected a CA certificate for an EAP network
404                // configuration.
405                enabled = false;
406            }
407            if (caCertSelection.equals(mUseSystemCertsString)
408                    && mEapDomainView != null
409                    && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
410                    && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
411                // Disallow submit if the user chooses to use system certificates for EAP server
412                // validation, but does not provide a domain.
413                enabled = false;
414            }
415        }
416        if (mEapUserCertSpinner != null
417                && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE
418                && ((String) mEapUserCertSpinner.getSelectedItem())
419                       .equals(mUnspecifiedCertString)) {
420            // Disallow submit if the user has not selected a user certificate for an EAP network
421            // configuration.
422            enabled = false;
423        }
424        return enabled;
425    }
426
427    void showWarningMessagesIfAppropriate() {
428        mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
429        mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
430
431        if (mEapCaCertSpinner != null
432                && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
433            String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
434            if (caCertSelection.equals(mDoNotValidateEapServerString)) {
435                // Display warning if user chooses not to validate the EAP server with a
436                // user-supplied CA certificate in an EAP network configuration.
437                mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
438            }
439            if (caCertSelection.equals(mUseSystemCertsString)
440                    && mEapDomainView != null
441                    && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
442                    && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
443                // Display warning if user chooses to use pre-installed public CA certificates
444                // without restricting the server domain that these certificates can be used to
445                // validate.
446                mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
447            }
448        }
449    }
450
451    /* package */ WifiConfiguration getConfig() {
452        if (mMode == WifiConfigUiBase.MODE_VIEW) {
453            return null;
454        }
455
456        WifiConfiguration config = new WifiConfiguration();
457
458        if (mAccessPoint == null) {
459            config.SSID = AccessPoint.convertToQuotedString(
460                    mSsidView.getText().toString());
461            // If the user adds a network manually, assume that it is hidden.
462            config.hiddenSSID = true;
463        } else if (!mAccessPoint.isSaved()) {
464            config.SSID = AccessPoint.convertToQuotedString(
465                    mAccessPoint.getSsidStr());
466        } else {
467            config.networkId = mAccessPoint.getConfig().networkId;
468        }
469
470        config.shared = mSharedCheckBox.isChecked();
471
472        switch (mAccessPointSecurity) {
473            case AccessPoint.SECURITY_NONE:
474                config.allowedKeyManagement.set(KeyMgmt.NONE);
475                break;
476
477            case AccessPoint.SECURITY_WEP:
478                config.allowedKeyManagement.set(KeyMgmt.NONE);
479                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
480                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
481                if (mPasswordView.length() != 0) {
482                    int length = mPasswordView.length();
483                    String password = mPasswordView.getText().toString();
484                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
485                    if ((length == 10 || length == 26 || length == 58)
486                            && password.matches("[0-9A-Fa-f]*")) {
487                        config.wepKeys[0] = password;
488                    } else {
489                        config.wepKeys[0] = '"' + password + '"';
490                    }
491                }
492                break;
493
494            case AccessPoint.SECURITY_PSK:
495                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
496                if (mPasswordView.length() != 0) {
497                    String password = mPasswordView.getText().toString();
498                    if (password.matches("[0-9A-Fa-f]{64}")) {
499                        config.preSharedKey = password;
500                    } else {
501                        config.preSharedKey = '"' + password + '"';
502                    }
503                }
504                break;
505
506            case AccessPoint.SECURITY_EAP:
507                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
508                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
509                config.enterpriseConfig = new WifiEnterpriseConfig();
510                int eapMethod = mEapMethodSpinner.getSelectedItemPosition();
511                int phase2Method = mPhase2Spinner.getSelectedItemPosition();
512                config.enterpriseConfig.setEapMethod(eapMethod);
513                switch (eapMethod) {
514                    case Eap.PEAP:
515                        // PEAP supports limited phase2 values
516                        // Map the index from the mPhase2PeapAdapter to the one used
517                        // by the API which has the full list of PEAP methods.
518                        switch(phase2Method) {
519                            case WIFI_PEAP_PHASE2_NONE:
520                                config.enterpriseConfig.setPhase2Method(Phase2.NONE);
521                                break;
522                            case WIFI_PEAP_PHASE2_MSCHAPV2:
523                                config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
524                                break;
525                            case WIFI_PEAP_PHASE2_GTC:
526                                config.enterpriseConfig.setPhase2Method(Phase2.GTC);
527                                break;
528                            default:
529                                Log.e(TAG, "Unknown phase2 method" + phase2Method);
530                                break;
531                        }
532                        break;
533                    default:
534                        // The default index from mPhase2FullAdapter maps to the API
535                        config.enterpriseConfig.setPhase2Method(phase2Method);
536                        break;
537                }
538
539                String caCert = (String) mEapCaCertSpinner.getSelectedItem();
540                config.enterpriseConfig.setCaCertificateAliases(null);
541                config.enterpriseConfig.setCaPath(null);
542                config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
543                if (caCert.equals(mUnspecifiedCertString)
544                        || caCert.equals(mDoNotValidateEapServerString)) {
545                    // ca_cert already set to null, so do nothing.
546                } else if (caCert.equals(mUseSystemCertsString)) {
547                    config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
548                } else if (caCert.equals(mMultipleCertSetString)) {
549                    if (mAccessPoint != null) {
550                        if (!mAccessPoint.isSaved()) {
551                            Log.e(TAG, "Multiple certs can only be set "
552                                    + "when editing saved network");
553                        }
554                        config.enterpriseConfig.setCaCertificateAliases(
555                                mAccessPoint
556                                        .getConfig()
557                                        .enterpriseConfig
558                                        .getCaCertificateAliases());
559                    }
560                } else {
561                    config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
562                }
563
564                // ca_cert or ca_path should not both be non-null, since we only intend to let
565                // the use either their own certificate, or the system certificates, not both.
566                // The variable that is not used must explicitly be set to null, so that a
567                // previously-set value on a saved configuration will be erased on an update.
568                if (config.enterpriseConfig.getCaCertificateAliases() != null
569                        && config.enterpriseConfig.getCaPath() != null) {
570                    Log.e(TAG, "ca_cert ("
571                            + config.enterpriseConfig.getCaCertificateAliases()
572                            + ") and ca_path ("
573                            + config.enterpriseConfig.getCaPath()
574                            + ") should not both be non-null");
575                }
576
577                String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
578                if (clientCert.equals(mUnspecifiedCertString)
579                        || clientCert.equals(mDoNotProvideEapUserCertString)) {
580                    // Note: |clientCert| should not be able to take the value |unspecifiedCert|,
581                    // since we prevent such configurations from being saved.
582                    clientCert = "";
583                }
584                config.enterpriseConfig.setClientCertificateAlias(clientCert);
585                if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) {
586                    config.enterpriseConfig.setIdentity("");
587                    config.enterpriseConfig.setAnonymousIdentity("");
588                } else if (eapMethod == Eap.PWD) {
589                    config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
590                    config.enterpriseConfig.setAnonymousIdentity("");
591                } else {
592                    config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
593                    config.enterpriseConfig.setAnonymousIdentity(
594                            mEapAnonymousView.getText().toString());
595                }
596
597                if (mPasswordView.isShown()) {
598                    // For security reasons, a previous password is not displayed to user.
599                    // Update only if it has been changed.
600                    if (mPasswordView.length() > 0) {
601                        config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
602                    }
603                } else {
604                    // clear password
605                    config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
606                }
607                break;
608            default:
609                return null;
610        }
611
612        config.setIpConfiguration(
613                new IpConfiguration(mIpAssignment, mProxySettings,
614                                    mStaticIpConfiguration, mHttpProxy));
615
616        return config;
617    }
618
619    private boolean ipAndProxyFieldsAreValid() {
620        mIpAssignment =
621                (mIpSettingsSpinner != null
622                    && mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP)
623                ? IpAssignment.STATIC
624                : IpAssignment.DHCP;
625
626        if (mIpAssignment == IpAssignment.STATIC) {
627            mStaticIpConfiguration = new StaticIpConfiguration();
628            int result = validateIpConfigFields(mStaticIpConfiguration);
629            if (result != 0) {
630                return false;
631            }
632        }
633
634        final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition();
635        mProxySettings = ProxySettings.NONE;
636        mHttpProxy = null;
637        if (selectedPosition == PROXY_STATIC && mProxyHostView != null) {
638            mProxySettings = ProxySettings.STATIC;
639            String host = mProxyHostView.getText().toString();
640            String portStr = mProxyPortView.getText().toString();
641            String exclusionList = mProxyExclusionListView.getText().toString();
642            int port = 0;
643            int result = 0;
644            try {
645                port = Integer.parseInt(portStr);
646                result = ProxySelector.validate(host, portStr, exclusionList);
647            } catch (NumberFormatException e) {
648                result = R.string.proxy_error_invalid_port;
649            }
650            if (result == 0) {
651                mHttpProxy = new ProxyInfo(host, port, exclusionList);
652            } else {
653                return false;
654            }
655        } else if (selectedPosition == PROXY_PAC && mProxyPacView != null) {
656            mProxySettings = ProxySettings.PAC;
657            CharSequence uriSequence = mProxyPacView.getText();
658            if (TextUtils.isEmpty(uriSequence)) {
659                return false;
660            }
661            Uri uri = Uri.parse(uriSequence.toString());
662            if (uri == null) {
663                return false;
664            }
665            mHttpProxy = new ProxyInfo(uri);
666        }
667        return true;
668    }
669
670    private Inet4Address getIPv4Address(String text) {
671        try {
672            return (Inet4Address) NetworkUtils.numericToInetAddress(text);
673        } catch (IllegalArgumentException | ClassCastException e) {
674            return null;
675        }
676    }
677
678    private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) {
679        if (mIpAddressView == null) return 0;
680
681        String ipAddr = mIpAddressView.getText().toString();
682        if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address;
683
684        Inet4Address inetAddr = getIPv4Address(ipAddr);
685        if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) {
686            return R.string.wifi_ip_settings_invalid_ip_address;
687        }
688
689        int networkPrefixLength = -1;
690        try {
691            networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
692            if (networkPrefixLength < 0 || networkPrefixLength > 32) {
693                return R.string.wifi_ip_settings_invalid_network_prefix_length;
694            }
695            staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength);
696        } catch (NumberFormatException e) {
697            // Set the hint as default after user types in ip address
698            mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
699                    R.string.wifi_network_prefix_length_hint));
700        } catch (IllegalArgumentException e) {
701            return R.string.wifi_ip_settings_invalid_ip_address;
702        }
703
704        String gateway = mGatewayView.getText().toString();
705        if (TextUtils.isEmpty(gateway)) {
706            try {
707                //Extract a default gateway from IP address
708                InetAddress netPart = NetworkUtils.getNetworkPart(inetAddr, networkPrefixLength);
709                byte[] addr = netPart.getAddress();
710                addr[addr.length - 1] = 1;
711                mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
712            } catch (RuntimeException ee) {
713            } catch (java.net.UnknownHostException u) {
714            }
715        } else {
716            InetAddress gatewayAddr = getIPv4Address(gateway);
717            if (gatewayAddr == null) {
718                return R.string.wifi_ip_settings_invalid_gateway;
719            }
720            if (gatewayAddr.isMulticastAddress()) {
721                return R.string.wifi_ip_settings_invalid_gateway;
722            }
723            staticIpConfiguration.gateway = gatewayAddr;
724        }
725
726        String dns = mDns1View.getText().toString();
727        InetAddress dnsAddr = null;
728
729        if (TextUtils.isEmpty(dns)) {
730            //If everything else is valid, provide hint as a default option
731            mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
732        } else {
733            dnsAddr = getIPv4Address(dns);
734            if (dnsAddr == null) {
735                return R.string.wifi_ip_settings_invalid_dns;
736            }
737            staticIpConfiguration.dnsServers.add(dnsAddr);
738        }
739
740        if (mDns2View.length() > 0) {
741            dns = mDns2View.getText().toString();
742            dnsAddr = getIPv4Address(dns);
743            if (dnsAddr == null) {
744                return R.string.wifi_ip_settings_invalid_dns;
745            }
746            staticIpConfiguration.dnsServers.add(dnsAddr);
747        }
748        return 0;
749    }
750
751    private void showSecurityFields() {
752        if (mAccessPointSecurity == AccessPoint.SECURITY_NONE) {
753            mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
754            return;
755        }
756        mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE);
757
758        if (mPasswordView == null) {
759            mPasswordView = (TextView) mView.findViewById(R.id.password);
760            mPasswordView.addTextChangedListener(this);
761            mPasswordView.setOnEditorActionListener(this);
762            mPasswordView.setOnKeyListener(this);
763            ((CheckBox) mView.findViewById(R.id.show_password))
764                .setOnCheckedChangeListener(this);
765
766            if (mAccessPoint != null && mAccessPoint.isSaved()) {
767                mPasswordView.setHint(R.string.wifi_unchanged);
768            }
769        }
770
771        if (mAccessPointSecurity != AccessPoint.SECURITY_EAP) {
772            mView.findViewById(R.id.eap).setVisibility(View.GONE);
773            return;
774        }
775        mView.findViewById(R.id.eap).setVisibility(View.VISIBLE);
776
777        if (mEapMethodSpinner == null) {
778            mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
779            mEapMethodSpinner.setOnItemSelectedListener(this);
780            if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean(
781                    com.android.internal.R.bool.config_eap_sim_based_auth_supported)) {
782                String[] eapMethods = mContext.getResources().getStringArray(
783                        R.array.eap_method_without_sim_auth);
784                ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext,
785                        android.R.layout.simple_spinner_item, eapMethods);
786                spinnerAdapter.setDropDownViewResource(
787                        android.R.layout.simple_spinner_dropdown_item);
788                mEapMethodSpinner.setAdapter(spinnerAdapter);
789            }
790            mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
791            mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
792            mEapCaCertSpinner.setOnItemSelectedListener(this);
793            mEapDomainView = (TextView) mView.findViewById(R.id.domain);
794            mEapDomainView.addTextChangedListener(this);
795            mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert);
796            mEapUserCertSpinner.setOnItemSelectedListener(this);
797            mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
798            mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous);
799
800            loadCertificates(
801                    mEapCaCertSpinner,
802                    Credentials.CA_CERTIFICATE,
803                    mDoNotValidateEapServerString,
804                    false,
805                    true);
806            loadCertificates(
807                    mEapUserCertSpinner,
808                    Credentials.USER_PRIVATE_KEY,
809                    mDoNotProvideEapUserCertString,
810                    false,
811                    false);
812
813            // Modifying an existing network
814            if (mAccessPoint != null && mAccessPoint.isSaved()) {
815                WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
816                int eapMethod = enterpriseConfig.getEapMethod();
817                int phase2Method = enterpriseConfig.getPhase2Method();
818                mEapMethodSpinner.setSelection(eapMethod);
819                showEapFieldsByMethod(eapMethod);
820                switch (eapMethod) {
821                    case Eap.PEAP:
822                        switch (phase2Method) {
823                            case Phase2.NONE:
824                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE);
825                                break;
826                            case Phase2.MSCHAPV2:
827                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
828                                break;
829                            case Phase2.GTC:
830                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC);
831                                break;
832                            default:
833                                Log.e(TAG, "Invalid phase 2 method " + phase2Method);
834                                break;
835                        }
836                        break;
837                    default:
838                        mPhase2Spinner.setSelection(phase2Method);
839                        break;
840                }
841                if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
842                    setSelection(mEapCaCertSpinner, mUseSystemCertsString);
843                } else {
844                    String[] caCerts = enterpriseConfig.getCaCertificateAliases();
845                    if (caCerts == null) {
846                        setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
847                    } else if (caCerts.length == 1) {
848                        setSelection(mEapCaCertSpinner, caCerts[0]);
849                    } else {
850                        // Reload the cert spinner with an extra "multiple certificates added" item.
851                        loadCertificates(
852                                mEapCaCertSpinner,
853                                Credentials.CA_CERTIFICATE,
854                                mDoNotValidateEapServerString,
855                                true,
856                                true);
857                        setSelection(mEapCaCertSpinner, mMultipleCertSetString);
858                    }
859                }
860                mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
861                String userCert = enterpriseConfig.getClientCertificateAlias();
862                if (TextUtils.isEmpty(userCert)) {
863                    setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString);
864                } else {
865                    setSelection(mEapUserCertSpinner, userCert);
866                }
867                mEapIdentityView.setText(enterpriseConfig.getIdentity());
868                mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
869            } else {
870                showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
871            }
872        } else {
873            showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
874        }
875    }
876
877    /**
878     * EAP-PWD valid fields include
879     *   identity
880     *   password
881     * EAP-PEAP valid fields include
882     *   phase2: MSCHAPV2, GTC
883     *   ca_cert
884     *   identity
885     *   anonymous_identity
886     *   password
887     * EAP-TLS valid fields include
888     *   user_cert
889     *   ca_cert
890     *   domain
891     *   identity
892     * EAP-TTLS valid fields include
893     *   phase2: PAP, MSCHAP, MSCHAPV2, GTC
894     *   ca_cert
895     *   identity
896     *   anonymous_identity
897     *   password
898     */
899    private void showEapFieldsByMethod(int eapMethod) {
900        // Common defaults
901        mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE);
902        mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
903        mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
904
905        // Defaults for most of the EAP methods and over-riden by
906        // by certain EAP methods
907        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
908        mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
909        mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
910
911        Context context = mConfigUi.getContext();
912        switch (eapMethod) {
913            case WIFI_EAP_METHOD_PWD:
914                setPhase2Invisible();
915                setCaCertInvisible();
916                setDomainInvisible();
917                setAnonymousIdentInvisible();
918                setUserCertInvisible();
919                break;
920            case WIFI_EAP_METHOD_TLS:
921                mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
922                setPhase2Invisible();
923                setAnonymousIdentInvisible();
924                setPasswordInvisible();
925                break;
926            case WIFI_EAP_METHOD_PEAP:
927                // Reset adapter if needed
928                if (mPhase2Adapter != mPhase2PeapAdapter) {
929                    mPhase2Adapter = mPhase2PeapAdapter;
930                    mPhase2Spinner.setAdapter(mPhase2Adapter);
931                }
932                mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
933                mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
934                setUserCertInvisible();
935                break;
936            case WIFI_EAP_METHOD_TTLS:
937                // Reset adapter if needed
938                if (mPhase2Adapter != mPhase2FullAdapter) {
939                    mPhase2Adapter = mPhase2FullAdapter;
940                    mPhase2Spinner.setAdapter(mPhase2Adapter);
941                }
942                mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
943                mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
944                setUserCertInvisible();
945                break;
946            case WIFI_EAP_METHOD_SIM:
947            case WIFI_EAP_METHOD_AKA:
948            case WIFI_EAP_METHOD_AKA_PRIME:
949                setPhase2Invisible();
950                setAnonymousIdentInvisible();
951                setCaCertInvisible();
952                setDomainInvisible();
953                setUserCertInvisible();
954                setPasswordInvisible();
955                setIdentityInvisible();
956                break;
957        }
958
959        if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
960            String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
961            if (eapCertSelection.equals(mDoNotValidateEapServerString)
962                    || eapCertSelection.equals(mUnspecifiedCertString)) {
963                // Domain suffix matching is not relevant if the user hasn't chosen a CA
964                // certificate yet, or chooses not to validate the EAP server.
965                setDomainInvisible();
966            }
967        }
968    }
969
970    private void setIdentityInvisible() {
971        mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
972        mPhase2Spinner.setSelection(Phase2.NONE);
973    }
974
975    private void setPhase2Invisible() {
976        mView.findViewById(R.id.l_phase2).setVisibility(View.GONE);
977        mPhase2Spinner.setSelection(Phase2.NONE);
978    }
979
980    private void setCaCertInvisible() {
981        mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE);
982        setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
983    }
984
985    private void setDomainInvisible() {
986        mView.findViewById(R.id.l_domain).setVisibility(View.GONE);
987        mEapDomainView.setText("");
988    }
989
990    private void setUserCertInvisible() {
991        mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE);
992        setSelection(mEapUserCertSpinner, mUnspecifiedCertString);
993    }
994
995    private void setAnonymousIdentInvisible() {
996        mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE);
997        mEapAnonymousView.setText("");
998    }
999
1000    private void setPasswordInvisible() {
1001        mPasswordView.setText("");
1002        mView.findViewById(R.id.password_layout).setVisibility(View.GONE);
1003        mView.findViewById(R.id.show_password_layout).setVisibility(View.GONE);
1004    }
1005
1006    private void showIpConfigFields() {
1007        WifiConfiguration config = null;
1008
1009        mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE);
1010
1011        if (mAccessPoint != null && mAccessPoint.isSaved()) {
1012            config = mAccessPoint.getConfig();
1013        }
1014
1015        if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) {
1016            mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE);
1017            if (mIpAddressView == null) {
1018                mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress);
1019                mIpAddressView.addTextChangedListener(this);
1020                mGatewayView = (TextView) mView.findViewById(R.id.gateway);
1021                mGatewayView.addTextChangedListener(this);
1022                mNetworkPrefixLengthView = (TextView) mView.findViewById(
1023                        R.id.network_prefix_length);
1024                mNetworkPrefixLengthView.addTextChangedListener(this);
1025                mDns1View = (TextView) mView.findViewById(R.id.dns1);
1026                mDns1View.addTextChangedListener(this);
1027                mDns2View = (TextView) mView.findViewById(R.id.dns2);
1028                mDns2View.addTextChangedListener(this);
1029            }
1030            if (config != null) {
1031                StaticIpConfiguration staticConfig = config.getStaticIpConfiguration();
1032                if (staticConfig != null) {
1033                    if (staticConfig.ipAddress != null) {
1034                        mIpAddressView.setText(
1035                                staticConfig.ipAddress.getAddress().getHostAddress());
1036                        mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress
1037                                .getNetworkPrefixLength()));
1038                    }
1039
1040                    if (staticConfig.gateway != null) {
1041                        mGatewayView.setText(staticConfig.gateway.getHostAddress());
1042                    }
1043
1044                    Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.iterator();
1045                    if (dnsIterator.hasNext()) {
1046                        mDns1View.setText(dnsIterator.next().getHostAddress());
1047                    }
1048                    if (dnsIterator.hasNext()) {
1049                        mDns2View.setText(dnsIterator.next().getHostAddress());
1050                    }
1051                }
1052            }
1053        } else {
1054            mView.findViewById(R.id.staticip).setVisibility(View.GONE);
1055        }
1056    }
1057
1058    private void showProxyFields() {
1059        WifiConfiguration config = null;
1060
1061        mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE);
1062
1063        if (mAccessPoint != null && mAccessPoint.isSaved()) {
1064            config = mAccessPoint.getConfig();
1065        }
1066
1067        if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) {
1068            setVisibility(R.id.proxy_warning_limited_support, View.VISIBLE);
1069            setVisibility(R.id.proxy_fields, View.VISIBLE);
1070            setVisibility(R.id.proxy_pac_field, View.GONE);
1071            if (mProxyHostView == null) {
1072                mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname);
1073                mProxyHostView.addTextChangedListener(this);
1074                mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port);
1075                mProxyPortView.addTextChangedListener(this);
1076                mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist);
1077                mProxyExclusionListView.addTextChangedListener(this);
1078            }
1079            if (config != null) {
1080                ProxyInfo proxyProperties = config.getHttpProxy();
1081                if (proxyProperties != null) {
1082                    mProxyHostView.setText(proxyProperties.getHost());
1083                    mProxyPortView.setText(Integer.toString(proxyProperties.getPort()));
1084                    mProxyExclusionListView.setText(proxyProperties.getExclusionListAsString());
1085                }
1086            }
1087        } else if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_PAC) {
1088            setVisibility(R.id.proxy_warning_limited_support, View.GONE);
1089            setVisibility(R.id.proxy_fields, View.GONE);
1090            setVisibility(R.id.proxy_pac_field, View.VISIBLE);
1091
1092            if (mProxyPacView == null) {
1093                mProxyPacView = (TextView) mView.findViewById(R.id.proxy_pac);
1094                mProxyPacView.addTextChangedListener(this);
1095            }
1096            if (config != null) {
1097                ProxyInfo proxyInfo = config.getHttpProxy();
1098                if (proxyInfo != null) {
1099                    mProxyPacView.setText(proxyInfo.getPacFileUrl().toString());
1100                }
1101            }
1102        } else {
1103            setVisibility(R.id.proxy_warning_limited_support, View.GONE);
1104            setVisibility(R.id.proxy_fields, View.GONE);
1105            setVisibility(R.id.proxy_pac_field, View.GONE);
1106        }
1107    }
1108
1109    private void setVisibility(int id, int visibility) {
1110        final View v = mView.findViewById(id);
1111        if (v != null) {
1112            v.setVisibility(visibility);
1113        }
1114    }
1115
1116    private void loadCertificates(
1117            Spinner spinner,
1118            String prefix,
1119            String noCertificateString,
1120            boolean showMultipleCerts,
1121            boolean showUsePreinstalledCertOption) {
1122        final Context context = mConfigUi.getContext();
1123
1124        ArrayList<String> certs = new ArrayList<String>();
1125        certs.add(mUnspecifiedCertString);
1126        if (showMultipleCerts) {
1127            certs.add(mMultipleCertSetString);
1128        }
1129        if (showUsePreinstalledCertOption) {
1130            certs.add(mUseSystemCertsString);
1131        }
1132        certs.addAll(
1133                Arrays.asList(KeyStore.getInstance().list(prefix, android.os.Process.WIFI_UID)));
1134        certs.add(noCertificateString);
1135
1136        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
1137                context, android.R.layout.simple_spinner_item,
1138                certs.toArray(new String[certs.size()]));
1139        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
1140        spinner.setAdapter(adapter);
1141    }
1142
1143    private void setSelection(Spinner spinner, String value) {
1144        if (value != null) {
1145            @SuppressWarnings("unchecked")
1146            ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
1147            for (int i = adapter.getCount() - 1; i >= 0; --i) {
1148                if (value.equals(adapter.getItem(i))) {
1149                    spinner.setSelection(i);
1150                    break;
1151                }
1152            }
1153        }
1154    }
1155
1156    public int getMode() {
1157        return mMode;
1158    }
1159
1160    @Override
1161    public void afterTextChanged(Editable s) {
1162        mTextViewChangedHandler.post(new Runnable() {
1163                public void run() {
1164                    showWarningMessagesIfAppropriate();
1165                    enableSubmitIfAppropriate();
1166                }
1167            });
1168    }
1169
1170    @Override
1171    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1172        // work done in afterTextChanged
1173    }
1174
1175    @Override
1176    public void onTextChanged(CharSequence s, int start, int before, int count) {
1177        // work done in afterTextChanged
1178    }
1179
1180    @Override
1181    public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
1182        if (textView == mPasswordView) {
1183            if (id == EditorInfo.IME_ACTION_DONE && isSubmittable()) {
1184                mConfigUi.dispatchSubmit();
1185                return true;
1186            }
1187        }
1188        return false;
1189    }
1190
1191    @Override
1192    public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
1193        if (view == mPasswordView) {
1194            if (keyCode == KeyEvent.KEYCODE_ENTER && isSubmittable()) {
1195                mConfigUi.dispatchSubmit();
1196                return true;
1197            }
1198        }
1199        return false;
1200    }
1201
1202    @Override
1203    public void onCheckedChanged(CompoundButton view, boolean isChecked) {
1204        if (view.getId() == R.id.show_password) {
1205            int pos = mPasswordView.getSelectionEnd();
1206            mPasswordView.setInputType(InputType.TYPE_CLASS_TEXT
1207                    | (isChecked ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
1208                                 : InputType.TYPE_TEXT_VARIATION_PASSWORD));
1209            if (pos >= 0) {
1210                ((EditText) mPasswordView).setSelection(pos);
1211            }
1212        } else if (view.getId() == R.id.wifi_advanced_togglebox) {
1213            if (isChecked) {
1214                mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
1215            } else {
1216                mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.GONE);
1217            }
1218        }
1219    }
1220
1221    @Override
1222    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1223        if (parent == mSecuritySpinner) {
1224            mAccessPointSecurity = position;
1225            showSecurityFields();
1226        } else if (parent == mEapMethodSpinner || parent == mEapCaCertSpinner) {
1227            showSecurityFields();
1228        } else if (parent == mProxySettingsSpinner) {
1229            showProxyFields();
1230        } else {
1231            showIpConfigFields();
1232        }
1233        showWarningMessagesIfAppropriate();
1234        enableSubmitIfAppropriate();
1235    }
1236
1237    @Override
1238    public void onNothingSelected(AdapterView<?> parent) {
1239        //
1240    }
1241
1242    /**
1243     * Make the characters of the password visible if show_password is checked.
1244     */
1245    public void updatePassword() {
1246        TextView passwdView = (TextView) mView.findViewById(R.id.password);
1247        passwdView.setInputType(InputType.TYPE_CLASS_TEXT
1248                | (((CheckBox) mView.findViewById(R.id.show_password)).isChecked()
1249                   ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
1250                   : InputType.TYPE_TEXT_VARIATION_PASSWORD));
1251    }
1252
1253    public AccessPoint getAccessPoint() {
1254        return mAccessPoint;
1255    }
1256}
1257