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 17 package com.android.settings.wifi; 18 19 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 20 21 import android.app.ActivityManager; 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.net.IpConfiguration; 25 import android.net.IpConfiguration.IpAssignment; 26 import android.net.IpConfiguration.ProxySettings; 27 import android.net.LinkAddress; 28 import android.net.NetworkInfo.DetailedState; 29 import android.net.NetworkUtils; 30 import android.net.ProxyInfo; 31 import android.net.RouteInfo; 32 import android.net.StaticIpConfiguration; 33 import android.net.Uri; 34 import android.net.wifi.WifiConfiguration; 35 import android.net.wifi.WifiConfiguration.AuthAlgorithm; 36 import android.net.wifi.WifiConfiguration.KeyMgmt; 37 import android.net.wifi.WifiEnterpriseConfig; 38 import android.net.wifi.WifiEnterpriseConfig.Eap; 39 import android.net.wifi.WifiEnterpriseConfig.Phase2; 40 import android.net.wifi.WifiInfo; 41 import android.os.Handler; 42 import android.os.UserHandle; 43 import android.security.Credentials; 44 import android.security.KeyStore; 45 import android.text.Editable; 46 import android.text.InputType; 47 import android.text.TextWatcher; 48 import android.text.TextUtils; 49 import android.util.Log; 50 import android.view.View; 51 import android.view.ViewGroup; 52 import android.widget.AdapterView; 53 import android.widget.ArrayAdapter; 54 import android.widget.Button; 55 import android.widget.CheckBox; 56 import android.widget.CompoundButton; 57 import android.widget.CompoundButton.OnCheckedChangeListener; 58 import android.widget.EditText; 59 import android.widget.Spinner; 60 import android.widget.TextView; 61 62 import com.android.settings.ProxySelector; 63 import com.android.settings.R; 64 65 import java.net.InetAddress; 66 import java.net.Inet4Address; 67 import 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 */ 73 public 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 WifiConfigController( WifiConfigUiBase parent, View view, AccessPoint accessPoint, boolean edit)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 addRow(ViewGroup group, int name, String value)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 getSignalString()330 private String getSignalString(){ 331 final int level = mAccessPoint.getLevel(); 332 333 return (level > -1 && level < mLevels.length) ? mLevels[level] : null; 334 } 335 hideSubmitButton()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 */ enableSubmitIfAppropriate()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 getConfig()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 ipAndProxyFieldsAreValid()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 getIPv4Address(String text)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 validateIpConfigFields(StaticIpConfiguration staticIpConfiguration)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 showSecurityFields()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 */ showEapFieldsByMethod(int eapMethod)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 setPhase2Invisible()768 private void setPhase2Invisible() { 769 mView.findViewById(R.id.l_phase2).setVisibility(View.GONE); 770 mPhase2Spinner.setSelection(Phase2.NONE); 771 } 772 setCaCertInvisible()773 private void setCaCertInvisible() { 774 mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE); 775 mEapCaCertSpinner.setSelection(unspecifiedCertIndex); 776 } 777 setUserCertInvisible()778 private void setUserCertInvisible() { 779 mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE); 780 mEapUserCertSpinner.setSelection(unspecifiedCertIndex); 781 } 782 setAnonymousIdentInvisible()783 private void setAnonymousIdentInvisible() { 784 mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE); 785 mEapAnonymousView.setText(""); 786 } 787 setPasswordInvisible()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 showIpConfigFields()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 showProxyFields()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 setVisibility(int id, int visibility)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 loadCertificates(Spinner spinner, String prefix)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 setSelection(Spinner spinner, String value)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 isEdit()936 public boolean isEdit() { 937 return mEdit; 938 } 939 940 @Override afterTextChanged(Editable s)941 public void afterTextChanged(Editable s) { 942 mTextViewChangedHandler.post(new Runnable() { 943 public void run() { 944 enableSubmitIfAppropriate(); 945 } 946 }); 947 } 948 949 @Override beforeTextChanged(CharSequence s, int start, int count, int after)950 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 951 // work done in afterTextChanged 952 } 953 954 @Override onTextChanged(CharSequence s, int start, int before, int count)955 public void onTextChanged(CharSequence s, int start, int before, int count) { 956 // work done in afterTextChanged 957 } 958 959 @Override onCheckedChanged(CompoundButton view, boolean isChecked)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 onItemSelected(AdapterView<?> parent, View view, int position, long id)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 onNothingSelected(AdapterView<?> parent)995 public void onNothingSelected(AdapterView<?> parent) { 996 // 997 } 998 999 /** 1000 * Make the characters of the password visible if show_password is checked. 1001 */ updatePasswordVisibility(boolean checked)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