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 static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20
21import android.app.ActivityManager;
22import android.content.Context;
23import android.content.res.Resources;
24import android.net.IpConfiguration;
25import android.net.IpConfiguration.IpAssignment;
26import android.net.IpConfiguration.ProxySettings;
27import android.net.LinkAddress;
28import android.net.NetworkInfo.DetailedState;
29import android.net.NetworkUtils;
30import android.net.ProxyInfo;
31import android.net.RouteInfo;
32import android.net.StaticIpConfiguration;
33import android.net.Uri;
34import android.net.wifi.WifiConfiguration;
35import android.net.wifi.WifiConfiguration.AuthAlgorithm;
36import android.net.wifi.WifiConfiguration.KeyMgmt;
37import android.net.wifi.WifiEnterpriseConfig;
38import android.net.wifi.WifiEnterpriseConfig.Eap;
39import android.net.wifi.WifiEnterpriseConfig.Phase2;
40import android.net.wifi.WifiInfo;
41import android.os.Handler;
42import android.os.UserHandle;
43import android.security.Credentials;
44import android.security.KeyStore;
45import android.text.Editable;
46import android.text.InputType;
47import android.text.TextWatcher;
48import android.text.TextUtils;
49import android.util.Log;
50import android.view.View;
51import android.view.ViewGroup;
52import android.widget.AdapterView;
53import android.widget.ArrayAdapter;
54import android.widget.Button;
55import android.widget.CheckBox;
56import android.widget.CompoundButton;
57import android.widget.CompoundButton.OnCheckedChangeListener;
58import android.widget.EditText;
59import android.widget.Spinner;
60import android.widget.TextView;
61
62import com.android.settings.ProxySelector;
63import com.android.settings.R;
64
65import java.net.InetAddress;
66import java.net.Inet4Address;
67import java.util.Iterator;
68
69/**
70 * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to
71 * share the logic for controlling buttons, text fields, etc.
72 */
73public class WifiConfigController implements TextWatcher,
74       AdapterView.OnItemSelectedListener, OnCheckedChangeListener {
75    private static final String TAG = "WifiConfigController";
76
77    private final WifiConfigUiBase mConfigUi;
78    private final View mView;
79    private final AccessPoint mAccessPoint;
80
81    /* This value comes from "wifi_ip_settings" resource array */
82    private static final int DHCP = 0;
83    private static final int STATIC_IP = 1;
84
85    /* These values come from "wifi_proxy_settings" resource array */
86    public static final int PROXY_NONE = 0;
87    public static final int PROXY_STATIC = 1;
88    public static final int PROXY_PAC = 2;
89
90    /* These values come from "wifi_eap_method" resource array */
91    public static final int WIFI_EAP_METHOD_PEAP = 0;
92    public static final int WIFI_EAP_METHOD_TLS  = 1;
93    public static final int WIFI_EAP_METHOD_TTLS = 2;
94    public static final int WIFI_EAP_METHOD_PWD  = 3;
95
96    /* These values come from "wifi_peap_phase2_entries" resource array */
97    public static final int WIFI_PEAP_PHASE2_NONE 	    = 0;
98    public static final int WIFI_PEAP_PHASE2_MSCHAPV2 	= 1;
99    public static final int WIFI_PEAP_PHASE2_GTC        = 2;
100
101    /* Phase2 methods supported by PEAP are limited */
102    private final ArrayAdapter<String> PHASE2_PEAP_ADAPTER;
103    /* Full list of phase2 methods */
104    private final ArrayAdapter<String> PHASE2_FULL_ADAPTER;
105
106    // True when this instance is used in SetupWizard XL context.
107    private final boolean mInXlSetupWizard;
108
109    private final Handler mTextViewChangedHandler;
110
111    // e.g. AccessPoint.SECURITY_NONE
112    private int mAccessPointSecurity;
113    private TextView mPasswordView;
114
115    private String unspecifiedCert = "unspecified";
116    private static final int unspecifiedCertIndex = 0;
117
118    private Spinner mSecuritySpinner;
119    private Spinner mEapMethodSpinner;
120    private Spinner mEapCaCertSpinner;
121    private Spinner mPhase2Spinner;
122    // Associated with mPhase2Spinner, one of PHASE2_FULL_ADAPTER or PHASE2_PEAP_ADAPTER
123    private ArrayAdapter<String> mPhase2Adapter;
124    private Spinner mEapUserCertSpinner;
125    private TextView mEapIdentityView;
126    private TextView mEapAnonymousView;
127
128    private Spinner mIpSettingsSpinner;
129    private TextView mIpAddressView;
130    private TextView mGatewayView;
131    private TextView mNetworkPrefixLengthView;
132    private TextView mDns1View;
133    private TextView mDns2View;
134
135    private Spinner mProxySettingsSpinner;
136    private TextView mProxyHostView;
137    private TextView mProxyPortView;
138    private TextView mProxyExclusionListView;
139    private TextView mProxyPacView;
140
141    private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED;
142    private ProxySettings mProxySettings = ProxySettings.UNASSIGNED;
143    private ProxyInfo mHttpProxy = null;
144    private StaticIpConfiguration mStaticIpConfiguration = null;
145
146    private String[] mLevels;
147    private boolean mEdit;
148    private TextView mSsidView;
149
150    private Context mContext;
151
152    public WifiConfigController(
153            WifiConfigUiBase parent, View view, AccessPoint accessPoint, boolean edit) {
154        mConfigUi = parent;
155        mInXlSetupWizard = (parent instanceof WifiConfigUiForSetupWizardXL);
156
157        mView = view;
158        mAccessPoint = accessPoint;
159        mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE :
160                accessPoint.security;
161        mEdit = edit;
162
163        mTextViewChangedHandler = new Handler();
164        mContext = mConfigUi.getContext();
165        final Resources res = mContext.getResources();
166
167        mLevels = res.getStringArray(R.array.wifi_signal);
168        PHASE2_PEAP_ADAPTER = new ArrayAdapter<String>(
169            mContext, android.R.layout.simple_spinner_item,
170            res.getStringArray(R.array.wifi_peap_phase2_entries));
171        PHASE2_PEAP_ADAPTER.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
172
173        PHASE2_FULL_ADAPTER = new ArrayAdapter<String>(
174                mContext, android.R.layout.simple_spinner_item,
175                res.getStringArray(R.array.wifi_phase2_entries));
176        PHASE2_FULL_ADAPTER.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
177
178        unspecifiedCert = mContext.getString(R.string.wifi_unspecified);
179        mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
180        mIpSettingsSpinner.setOnItemSelectedListener(this);
181        mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
182        mProxySettingsSpinner.setOnItemSelectedListener(this);
183
184        if (mAccessPoint == null) { // new network
185            mConfigUi.setTitle(R.string.wifi_add_network);
186
187            mSsidView = (TextView) mView.findViewById(R.id.ssid);
188            mSsidView.addTextChangedListener(this);
189            mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security));
190            mSecuritySpinner.setOnItemSelectedListener(this);
191            if (mInXlSetupWizard) {
192                mView.findViewById(R.id.type_ssid).setVisibility(View.VISIBLE);
193                mView.findViewById(R.id.type_security).setVisibility(View.VISIBLE);
194                // We want custom layout. The content must be same as the other cases.
195
196                ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,
197                        R.layout.wifi_setup_custom_list_item_1, android.R.id.text1,
198                        res.getStringArray(R.array.wifi_security_no_eap));
199                mSecuritySpinner.setAdapter(adapter);
200            } else {
201                mView.findViewById(R.id.type).setVisibility(View.VISIBLE);
202            }
203
204            showIpConfigFields();
205            showProxyFields();
206            mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
207            ((CheckBox)mView.findViewById(R.id.wifi_advanced_togglebox))
208                    .setOnCheckedChangeListener(this);
209
210
211            mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
212        } else {
213            mConfigUi.setTitle(mAccessPoint.ssid);
214
215            ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
216
217            boolean showAdvancedFields = false;
218            if (mAccessPoint.networkId != INVALID_NETWORK_ID) {
219                WifiConfiguration config = mAccessPoint.getConfig();
220                if (config.getIpAssignment() == IpAssignment.STATIC) {
221                    mIpSettingsSpinner.setSelection(STATIC_IP);
222                    showAdvancedFields = true;
223                    // Display IP address.
224                    StaticIpConfiguration staticConfig = config.getStaticIpConfiguration();
225                    if (staticConfig != null && staticConfig.ipAddress != null) {
226                        addRow(group, R.string.wifi_ip_address,
227                           staticConfig.ipAddress.getAddress().getHostAddress());
228                    }
229                } else {
230                    mIpSettingsSpinner.setSelection(DHCP);
231                }
232
233
234                if (config.getProxySettings() == ProxySettings.STATIC) {
235                    mProxySettingsSpinner.setSelection(PROXY_STATIC);
236                    showAdvancedFields = true;
237                } else if (config.getProxySettings() == ProxySettings.PAC) {
238                    mProxySettingsSpinner.setSelection(PROXY_PAC);
239                    showAdvancedFields = true;
240                } else {
241                    mProxySettingsSpinner.setSelection(PROXY_NONE);
242                }
243            }
244
245            if ((mAccessPoint.networkId == INVALID_NETWORK_ID && !mAccessPoint.isActive())
246                    || mEdit) {
247                showSecurityFields();
248                showIpConfigFields();
249                showProxyFields();
250                mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
251                ((CheckBox)mView.findViewById(R.id.wifi_advanced_togglebox))
252                    .setOnCheckedChangeListener(this);
253                if (showAdvancedFields) {
254                    ((CheckBox)mView.findViewById(R.id.wifi_advanced_togglebox)).setChecked(true);
255                    mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
256                }
257            }
258
259            if (mEdit) {
260                mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
261            } else {
262                final DetailedState state = mAccessPoint.getState();
263                final String signalLevel = getSignalString();
264
265                if (state == null && signalLevel != null) {
266                    mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect));
267                } else {
268                    if (state != null) {
269                        addRow(group, R.string.wifi_status, Summary.get(mConfigUi.getContext(),
270                                state, mAccessPoint.networkId ==
271                                WifiConfiguration.INVALID_NETWORK_ID));
272                    }
273
274                    if (signalLevel != null) {
275                        addRow(group, R.string.wifi_signal, signalLevel);
276                    }
277
278                    WifiInfo info = mAccessPoint.getInfo();
279                    if (info != null && info.getLinkSpeed() != -1) {
280                        addRow(group, R.string.wifi_speed, info.getLinkSpeed()
281                                + WifiInfo.LINK_SPEED_UNITS);
282                    }
283
284                    if (info != null && info.getFrequency() != -1) {
285                        final int frequency = info.getFrequency();
286                        String band = null;
287
288                        if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
289                                && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
290                            band = res.getString(R.string.wifi_band_24ghz);
291                        } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
292                                && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
293                            band = res.getString(R.string.wifi_band_5ghz);
294                        } else {
295                            Log.e(TAG, "Unexpected frequency " + frequency);
296                        }
297                        if (band != null) {
298                            addRow(group, R.string.wifi_frequency, band);
299                        }
300                    }
301
302                    addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false));
303                    mView.findViewById(R.id.ip_fields).setVisibility(View.GONE);
304                }
305                if ((mAccessPoint.networkId != INVALID_NETWORK_ID || mAccessPoint.isActive())
306                        && ActivityManager.getCurrentUser() == UserHandle.USER_OWNER) {
307                    mConfigUi.setForgetButton(res.getString(R.string.wifi_forget));
308                }
309            }
310        }
311
312        if ((mEdit) || (mAccessPoint != null
313                && mAccessPoint.getState() == null && mAccessPoint.getLevel() != -1)){
314            mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel));
315        }else{
316            mConfigUi.setCancelButton(res.getString(R.string.wifi_display_options_done));
317        }
318        if (mConfigUi.getSubmitButton() != null) {
319            enableSubmitIfAppropriate();
320        }
321    }
322
323    private void addRow(ViewGroup group, int name, String value) {
324        View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false);
325        ((TextView) row.findViewById(R.id.name)).setText(name);
326        ((TextView) row.findViewById(R.id.value)).setText(value);
327        group.addView(row);
328    }
329
330    private String getSignalString(){
331        final int level = mAccessPoint.getLevel();
332
333        return (level > -1 && level < mLevels.length) ? mLevels[level] : null;
334    }
335
336    void hideSubmitButton() {
337        Button submit = mConfigUi.getSubmitButton();
338        if (submit == null) return;
339
340        submit.setVisibility(View.GONE);
341    }
342
343    /* show submit button if password, ip and proxy settings are valid */
344    void enableSubmitIfAppropriate() {
345        Button submit = mConfigUi.getSubmitButton();
346        if (submit == null) return;
347
348        boolean enabled = false;
349        boolean passwordInvalid = false;
350
351        if (mPasswordView != null &&
352            ((mAccessPointSecurity == AccessPoint.SECURITY_WEP && mPasswordView.length() == 0) ||
353            (mAccessPointSecurity == AccessPoint.SECURITY_PSK && mPasswordView.length() < 8))) {
354            passwordInvalid = true;
355        }
356
357        if ((mSsidView != null && mSsidView.length() == 0) ||
358            ((mAccessPoint == null || mAccessPoint.networkId == INVALID_NETWORK_ID) &&
359            passwordInvalid)) {
360            enabled = false;
361        } else {
362            if (ipAndProxyFieldsAreValid()) {
363                enabled = true;
364            } else {
365                enabled = false;
366            }
367        }
368        submit.setEnabled(enabled);
369    }
370
371    /* package */ WifiConfiguration getConfig() {
372        if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID && !mEdit) {
373            return null;
374        }
375
376        WifiConfiguration config = new WifiConfiguration();
377
378        if (mAccessPoint == null) {
379            config.SSID = AccessPoint.convertToQuotedString(
380                    mSsidView.getText().toString());
381            // If the user adds a network manually, assume that it is hidden.
382            config.hiddenSSID = true;
383        } else if (mAccessPoint.networkId == INVALID_NETWORK_ID) {
384            config.SSID = AccessPoint.convertToQuotedString(
385                    mAccessPoint.ssid);
386        } else {
387            config.networkId = mAccessPoint.networkId;
388        }
389
390        switch (mAccessPointSecurity) {
391            case AccessPoint.SECURITY_NONE:
392                config.allowedKeyManagement.set(KeyMgmt.NONE);
393                break;
394
395            case AccessPoint.SECURITY_WEP:
396                config.allowedKeyManagement.set(KeyMgmt.NONE);
397                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
398                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
399                if (mPasswordView.length() != 0) {
400                    int length = mPasswordView.length();
401                    String password = mPasswordView.getText().toString();
402                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
403                    if ((length == 10 || length == 26 || length == 58) &&
404                            password.matches("[0-9A-Fa-f]*")) {
405                        config.wepKeys[0] = password;
406                    } else {
407                        config.wepKeys[0] = '"' + password + '"';
408                    }
409                }
410                break;
411
412            case AccessPoint.SECURITY_PSK:
413                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
414                if (mPasswordView.length() != 0) {
415                    String password = mPasswordView.getText().toString();
416                    if (password.matches("[0-9A-Fa-f]{64}")) {
417                        config.preSharedKey = password;
418                    } else {
419                        config.preSharedKey = '"' + password + '"';
420                    }
421                }
422                break;
423
424            case AccessPoint.SECURITY_EAP:
425                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
426                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
427                config.enterpriseConfig = new WifiEnterpriseConfig();
428                int eapMethod = mEapMethodSpinner.getSelectedItemPosition();
429                int phase2Method = mPhase2Spinner.getSelectedItemPosition();
430                config.enterpriseConfig.setEapMethod(eapMethod);
431                switch (eapMethod) {
432                    case Eap.PEAP:
433                        // PEAP supports limited phase2 values
434                        // Map the index from the PHASE2_PEAP_ADAPTER to the one used
435                        // by the API which has the full list of PEAP methods.
436                        switch(phase2Method) {
437                            case WIFI_PEAP_PHASE2_NONE:
438                                config.enterpriseConfig.setPhase2Method(Phase2.NONE);
439                                break;
440                            case WIFI_PEAP_PHASE2_MSCHAPV2:
441                                config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
442                                break;
443                            case WIFI_PEAP_PHASE2_GTC:
444                                config.enterpriseConfig.setPhase2Method(Phase2.GTC);
445                                break;
446                            default:
447                                Log.e(TAG, "Unknown phase2 method" + phase2Method);
448                                break;
449                        }
450                        break;
451                    default:
452                        // The default index from PHASE2_FULL_ADAPTER maps to the API
453                        config.enterpriseConfig.setPhase2Method(phase2Method);
454                        break;
455                }
456                String caCert = (String) mEapCaCertSpinner.getSelectedItem();
457                if (caCert.equals(unspecifiedCert)) caCert = "";
458                config.enterpriseConfig.setCaCertificateAlias(caCert);
459                String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
460                if (clientCert.equals(unspecifiedCert)) clientCert = "";
461                config.enterpriseConfig.setClientCertificateAlias(clientCert);
462                config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
463                config.enterpriseConfig.setAnonymousIdentity(
464                        mEapAnonymousView.getText().toString());
465
466                if (mPasswordView.isShown()) {
467                    // For security reasons, a previous password is not displayed to user.
468                    // Update only if it has been changed.
469                    if (mPasswordView.length() > 0) {
470                        config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
471                    }
472                } else {
473                    // clear password
474                    config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
475                }
476                break;
477            default:
478                return null;
479        }
480
481        config.setIpConfiguration(
482                new IpConfiguration(mIpAssignment, mProxySettings,
483                                    mStaticIpConfiguration, mHttpProxy));
484
485        return config;
486    }
487
488    private boolean ipAndProxyFieldsAreValid() {
489        mIpAssignment = (mIpSettingsSpinner != null &&
490                mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) ?
491                IpAssignment.STATIC : IpAssignment.DHCP;
492
493        if (mIpAssignment == IpAssignment.STATIC) {
494            mStaticIpConfiguration = new StaticIpConfiguration();
495            int result = validateIpConfigFields(mStaticIpConfiguration);
496            if (result != 0) {
497                return false;
498            }
499        }
500
501        final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition();
502        mProxySettings = ProxySettings.NONE;
503        mHttpProxy = null;
504        if (selectedPosition == PROXY_STATIC && mProxyHostView != null) {
505            mProxySettings = ProxySettings.STATIC;
506            String host = mProxyHostView.getText().toString();
507            String portStr = mProxyPortView.getText().toString();
508            String exclusionList = mProxyExclusionListView.getText().toString();
509            int port = 0;
510            int result = 0;
511            try {
512                port = Integer.parseInt(portStr);
513                result = ProxySelector.validate(host, portStr, exclusionList);
514            } catch (NumberFormatException e) {
515                result = R.string.proxy_error_invalid_port;
516            }
517            if (result == 0) {
518                mHttpProxy = new ProxyInfo(host, port, exclusionList);
519            } else {
520                return false;
521            }
522        } else if (selectedPosition == PROXY_PAC && mProxyPacView != null) {
523            mProxySettings = ProxySettings.PAC;
524            CharSequence uriSequence = mProxyPacView.getText();
525            if (TextUtils.isEmpty(uriSequence)) {
526                return false;
527            }
528            Uri uri = Uri.parse(uriSequence.toString());
529            if (uri == null) {
530                return false;
531            }
532            mHttpProxy = new ProxyInfo(uri);
533        }
534        return true;
535    }
536
537    private Inet4Address getIPv4Address(String text) {
538        try {
539            return (Inet4Address) NetworkUtils.numericToInetAddress(text);
540        } catch (IllegalArgumentException|ClassCastException e) {
541            return null;
542        }
543    }
544
545    private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) {
546        if (mIpAddressView == null) return 0;
547
548        String ipAddr = mIpAddressView.getText().toString();
549        if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address;
550
551        Inet4Address inetAddr = getIPv4Address(ipAddr);
552        if (inetAddr == null) {
553            return R.string.wifi_ip_settings_invalid_ip_address;
554        }
555
556        int networkPrefixLength = -1;
557        try {
558            networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
559            if (networkPrefixLength < 0 || networkPrefixLength > 32) {
560                return R.string.wifi_ip_settings_invalid_network_prefix_length;
561            }
562            staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength);
563        } catch (NumberFormatException e) {
564            // Set the hint as default after user types in ip address
565            mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
566                    R.string.wifi_network_prefix_length_hint));
567        }
568
569        String gateway = mGatewayView.getText().toString();
570        if (TextUtils.isEmpty(gateway)) {
571            try {
572                //Extract a default gateway from IP address
573                InetAddress netPart = NetworkUtils.getNetworkPart(inetAddr, networkPrefixLength);
574                byte[] addr = netPart.getAddress();
575                addr[addr.length-1] = 1;
576                mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
577            } catch (RuntimeException ee) {
578            } catch (java.net.UnknownHostException u) {
579            }
580        } else {
581            InetAddress gatewayAddr = getIPv4Address(gateway);
582            if (gatewayAddr == null) {
583                return R.string.wifi_ip_settings_invalid_gateway;
584            }
585            staticIpConfiguration.gateway = gatewayAddr;
586        }
587
588        String dns = mDns1View.getText().toString();
589        InetAddress dnsAddr = null;
590
591        if (TextUtils.isEmpty(dns)) {
592            //If everything else is valid, provide hint as a default option
593            mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
594        } else {
595            dnsAddr = getIPv4Address(dns);
596            if (dnsAddr == null) {
597                return R.string.wifi_ip_settings_invalid_dns;
598            }
599            staticIpConfiguration.dnsServers.add(dnsAddr);
600        }
601
602        if (mDns2View.length() > 0) {
603            dns = mDns2View.getText().toString();
604            dnsAddr = getIPv4Address(dns);
605            if (dnsAddr == null) {
606                return R.string.wifi_ip_settings_invalid_dns;
607            }
608            staticIpConfiguration.dnsServers.add(dnsAddr);
609        }
610        return 0;
611    }
612
613    private void showSecurityFields() {
614        if (mInXlSetupWizard) {
615            // Note: XL SetupWizard won't hide "EAP" settings here.
616            if (!((WifiSettingsForSetupWizardXL)mConfigUi.getContext()).initSecurityFields(mView,
617                        mAccessPointSecurity)) {
618                return;
619            }
620        }
621        if (mAccessPointSecurity == AccessPoint.SECURITY_NONE) {
622            mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
623            return;
624        }
625        mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE);
626
627        if (mPasswordView == null) {
628            mPasswordView = (TextView) mView.findViewById(R.id.password);
629            mPasswordView.addTextChangedListener(this);
630            ((CheckBox) mView.findViewById(R.id.show_password))
631                .setOnCheckedChangeListener(this);
632
633            if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) {
634                mPasswordView.setHint(R.string.wifi_unchanged);
635            }
636        }
637
638        if (mAccessPointSecurity != AccessPoint.SECURITY_EAP) {
639            mView.findViewById(R.id.eap).setVisibility(View.GONE);
640            return;
641        }
642        mView.findViewById(R.id.eap).setVisibility(View.VISIBLE);
643
644        if (mEapMethodSpinner == null) {
645            mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
646            mEapMethodSpinner.setOnItemSelectedListener(this);
647            mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
648            mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
649            mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert);
650            mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
651            mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous);
652
653            loadCertificates(mEapCaCertSpinner, Credentials.CA_CERTIFICATE);
654            loadCertificates(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY);
655
656            // Modifying an existing network
657            if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) {
658                WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
659                int eapMethod = enterpriseConfig.getEapMethod();
660                int phase2Method = enterpriseConfig.getPhase2Method();
661                mEapMethodSpinner.setSelection(eapMethod);
662                showEapFieldsByMethod(eapMethod);
663                switch (eapMethod) {
664                    case Eap.PEAP:
665                        switch (phase2Method) {
666                            case Phase2.NONE:
667                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE);
668                                break;
669                            case Phase2.MSCHAPV2:
670                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
671                                break;
672                            case Phase2.GTC:
673                                mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC);
674                                break;
675                            default:
676                                Log.e(TAG, "Invalid phase 2 method " + phase2Method);
677                                break;
678                        }
679                        break;
680                    default:
681                        mPhase2Spinner.setSelection(phase2Method);
682                        break;
683                }
684                setSelection(mEapCaCertSpinner, enterpriseConfig.getCaCertificateAlias());
685                setSelection(mEapUserCertSpinner, enterpriseConfig.getClientCertificateAlias());
686                mEapIdentityView.setText(enterpriseConfig.getIdentity());
687                mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
688            } else {
689                // Choose a default for a new network and show only appropriate
690                // fields
691                mEapMethodSpinner.setSelection(Eap.PEAP);
692                showEapFieldsByMethod(Eap.PEAP);
693            }
694        } else {
695            showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
696        }
697    }
698
699    /**
700     * EAP-PWD valid fields include
701     *   identity
702     *   password
703     * EAP-PEAP valid fields include
704     *   phase2: MSCHAPV2, GTC
705     *   ca_cert
706     *   identity
707     *   anonymous_identity
708     *   password
709     * EAP-TLS valid fields include
710     *   user_cert
711     *   ca_cert
712     *   identity
713     * EAP-TTLS valid fields include
714     *   phase2: PAP, MSCHAP, MSCHAPV2, GTC
715     *   ca_cert
716     *   identity
717     *   anonymous_identity
718     *   password
719     */
720    private void showEapFieldsByMethod(int eapMethod) {
721        // Common defaults
722        mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE);
723        mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
724
725        // Defaults for most of the EAP methods and over-riden by
726        // by certain EAP methods
727        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
728        mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
729        mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
730
731        Context context = mConfigUi.getContext();
732        switch (eapMethod) {
733            case WIFI_EAP_METHOD_PWD:
734                setPhase2Invisible();
735                setCaCertInvisible();
736                setAnonymousIdentInvisible();
737                setUserCertInvisible();
738                break;
739            case WIFI_EAP_METHOD_TLS:
740                mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
741                setPhase2Invisible();
742                setAnonymousIdentInvisible();
743                setPasswordInvisible();
744                break;
745            case WIFI_EAP_METHOD_PEAP:
746                // Reset adapter if needed
747                if (mPhase2Adapter != PHASE2_PEAP_ADAPTER) {
748                    mPhase2Adapter = PHASE2_PEAP_ADAPTER;
749                    mPhase2Spinner.setAdapter(mPhase2Adapter);
750                }
751                mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
752                mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
753                setUserCertInvisible();
754                break;
755            case WIFI_EAP_METHOD_TTLS:
756                // Reset adapter if needed
757                if (mPhase2Adapter != PHASE2_FULL_ADAPTER) {
758                    mPhase2Adapter = PHASE2_FULL_ADAPTER;
759                    mPhase2Spinner.setAdapter(mPhase2Adapter);
760                }
761                mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
762                mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
763                setUserCertInvisible();
764                break;
765        }
766    }
767
768    private void setPhase2Invisible() {
769        mView.findViewById(R.id.l_phase2).setVisibility(View.GONE);
770        mPhase2Spinner.setSelection(Phase2.NONE);
771    }
772
773    private void setCaCertInvisible() {
774        mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE);
775        mEapCaCertSpinner.setSelection(unspecifiedCertIndex);
776    }
777
778    private void setUserCertInvisible() {
779        mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE);
780        mEapUserCertSpinner.setSelection(unspecifiedCertIndex);
781    }
782
783    private void setAnonymousIdentInvisible() {
784        mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE);
785        mEapAnonymousView.setText("");
786    }
787
788    private void setPasswordInvisible() {
789        mPasswordView.setText("");
790        mView.findViewById(R.id.password_layout).setVisibility(View.GONE);
791        mView.findViewById(R.id.show_password_layout).setVisibility(View.GONE);
792    }
793
794    private void showIpConfigFields() {
795        WifiConfiguration config = null;
796
797        mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE);
798
799        if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) {
800            config = mAccessPoint.getConfig();
801        }
802
803        if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) {
804            mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE);
805            if (mIpAddressView == null) {
806                mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress);
807                mIpAddressView.addTextChangedListener(this);
808                mGatewayView = (TextView) mView.findViewById(R.id.gateway);
809                mGatewayView.addTextChangedListener(this);
810                mNetworkPrefixLengthView = (TextView) mView.findViewById(
811                        R.id.network_prefix_length);
812                mNetworkPrefixLengthView.addTextChangedListener(this);
813                mDns1View = (TextView) mView.findViewById(R.id.dns1);
814                mDns1View.addTextChangedListener(this);
815                mDns2View = (TextView) mView.findViewById(R.id.dns2);
816                mDns2View.addTextChangedListener(this);
817            }
818            if (config != null) {
819                StaticIpConfiguration staticConfig = config.getStaticIpConfiguration();
820                if (staticConfig != null) {
821                    if (staticConfig.ipAddress != null) {
822                        mIpAddressView.setText(
823                                staticConfig.ipAddress.getAddress().getHostAddress());
824                        mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress
825                                .getNetworkPrefixLength()));
826                    }
827
828                    if (staticConfig.gateway != null) {
829                        mGatewayView.setText(staticConfig.gateway.getHostAddress());
830                    }
831
832                    Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.iterator();
833                    if (dnsIterator.hasNext()) {
834                        mDns1View.setText(dnsIterator.next().getHostAddress());
835                    }
836                    if (dnsIterator.hasNext()) {
837                        mDns2View.setText(dnsIterator.next().getHostAddress());
838                    }
839                }
840            }
841        } else {
842            mView.findViewById(R.id.staticip).setVisibility(View.GONE);
843        }
844    }
845
846    private void showProxyFields() {
847        WifiConfiguration config = null;
848
849        mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE);
850
851        if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) {
852            config = mAccessPoint.getConfig();
853        }
854
855        if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) {
856            setVisibility(R.id.proxy_warning_limited_support, View.VISIBLE);
857            setVisibility(R.id.proxy_fields, View.VISIBLE);
858            setVisibility(R.id.proxy_pac_field, View.GONE);
859            if (mProxyHostView == null) {
860                mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname);
861                mProxyHostView.addTextChangedListener(this);
862                mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port);
863                mProxyPortView.addTextChangedListener(this);
864                mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist);
865                mProxyExclusionListView.addTextChangedListener(this);
866            }
867            if (config != null) {
868                ProxyInfo proxyProperties = config.getHttpProxy();
869                if (proxyProperties != null) {
870                    mProxyHostView.setText(proxyProperties.getHost());
871                    mProxyPortView.setText(Integer.toString(proxyProperties.getPort()));
872                    mProxyExclusionListView.setText(proxyProperties.getExclusionListAsString());
873                }
874            }
875        } else if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_PAC) {
876            setVisibility(R.id.proxy_warning_limited_support, View.GONE);
877            setVisibility(R.id.proxy_fields, View.GONE);
878            setVisibility(R.id.proxy_pac_field, View.VISIBLE);
879
880            if (mProxyPacView == null) {
881                mProxyPacView = (TextView) mView.findViewById(R.id.proxy_pac);
882                mProxyPacView.addTextChangedListener(this);
883            }
884            if (config != null) {
885                ProxyInfo proxyInfo = config.getHttpProxy();
886                if (proxyInfo != null) {
887                    mProxyPacView.setText(proxyInfo.getPacFileUrl().toString());
888                }
889            }
890        } else {
891            setVisibility(R.id.proxy_warning_limited_support, View.GONE);
892            setVisibility(R.id.proxy_fields, View.GONE);
893            setVisibility(R.id.proxy_pac_field, View.GONE);
894        }
895    }
896
897    private void setVisibility(int id, int visibility) {
898        final View v = mView.findViewById(id);
899        if (v != null) {
900            v.setVisibility(visibility);
901        }
902    }
903
904    private void loadCertificates(Spinner spinner, String prefix) {
905        final Context context = mConfigUi.getContext();
906
907        String[] certs = KeyStore.getInstance().saw(prefix, android.os.Process.WIFI_UID);
908        if (certs == null || certs.length == 0) {
909            certs = new String[] {unspecifiedCert};
910        } else {
911            final String[] array = new String[certs.length + 1];
912            array[0] = unspecifiedCert;
913            System.arraycopy(certs, 0, array, 1, certs.length);
914            certs = array;
915        }
916
917        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
918                context, android.R.layout.simple_spinner_item, certs);
919        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
920        spinner.setAdapter(adapter);
921    }
922
923    private void setSelection(Spinner spinner, String value) {
924        if (value != null) {
925            @SuppressWarnings("unchecked")
926            ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
927            for (int i = adapter.getCount() - 1; i >= 0; --i) {
928                if (value.equals(adapter.getItem(i))) {
929                    spinner.setSelection(i);
930                    break;
931                }
932            }
933        }
934    }
935
936    public boolean isEdit() {
937        return mEdit;
938    }
939
940    @Override
941    public void afterTextChanged(Editable s) {
942        mTextViewChangedHandler.post(new Runnable() {
943                public void run() {
944                    enableSubmitIfAppropriate();
945                }
946            });
947    }
948
949    @Override
950    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
951        // work done in afterTextChanged
952    }
953
954    @Override
955    public void onTextChanged(CharSequence s, int start, int before, int count) {
956        // work done in afterTextChanged
957    }
958
959    @Override
960    public void onCheckedChanged(CompoundButton view, boolean isChecked) {
961        if (view.getId() == R.id.show_password) {
962            int pos = mPasswordView.getSelectionEnd();
963            mPasswordView.setInputType(
964                    InputType.TYPE_CLASS_TEXT | (isChecked ?
965                            InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD :
966                                InputType.TYPE_TEXT_VARIATION_PASSWORD));
967            if (pos >= 0) {
968                ((EditText)mPasswordView).setSelection(pos);
969            }
970        } else if (view.getId() == R.id.wifi_advanced_togglebox) {
971            if (isChecked) {
972                mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
973            } else {
974                mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.GONE);
975            }
976        }
977    }
978
979    @Override
980    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
981        if (parent == mSecuritySpinner) {
982            mAccessPointSecurity = position;
983            showSecurityFields();
984        } else if (parent == mEapMethodSpinner) {
985            showSecurityFields();
986        } else if (parent == mProxySettingsSpinner) {
987            showProxyFields();
988        } else {
989            showIpConfigFields();
990        }
991        enableSubmitIfAppropriate();
992    }
993
994    @Override
995    public void onNothingSelected(AdapterView<?> parent) {
996        //
997    }
998
999    /**
1000     * Make the characters of the password visible if show_password is checked.
1001     */
1002    private void updatePasswordVisibility(boolean checked) {
1003        int pos = mPasswordView.getSelectionEnd();
1004        mPasswordView.setInputType(
1005                InputType.TYPE_CLASS_TEXT | (checked ?
1006                        InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD :
1007                            InputType.TYPE_TEXT_VARIATION_PASSWORD));
1008        if (pos >= 0) {
1009            ((EditText)mPasswordView).setSelection(pos);
1010        }
1011    }
1012}
1013