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 com.android.settings.R; 20 21 import android.app.AlertDialog; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.res.Resources; 25 import android.net.NetworkInfo.DetailedState; 26 import android.net.wifi.WifiConfiguration; 27 import android.net.wifi.WifiConfiguration.AuthAlgorithm; 28 import android.net.wifi.WifiConfiguration.KeyMgmt; 29 import android.net.wifi.WifiInfo; 30 import android.os.Bundle; 31 import android.security.Credentials; 32 import android.security.KeyStore; 33 import android.text.Editable; 34 import android.text.InputType; 35 import android.text.TextWatcher; 36 import android.text.format.Formatter; 37 import android.view.View; 38 import android.view.ViewGroup; 39 import android.widget.AdapterView; 40 import android.widget.ArrayAdapter; 41 import android.widget.CheckBox; 42 import android.widget.Spinner; 43 import android.widget.TextView; 44 45 class WifiDialog extends AlertDialog implements View.OnClickListener, 46 TextWatcher, AdapterView.OnItemSelectedListener { 47 private static final String KEYSTORE_SPACE = "keystore://"; 48 49 static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE; 50 static final int BUTTON_FORGET = DialogInterface.BUTTON_NEUTRAL; 51 52 final boolean edit; 53 private final DialogInterface.OnClickListener mListener; 54 private final AccessPoint mAccessPoint; 55 56 private View mView; 57 private TextView mSsid; 58 private int mSecurity; 59 private TextView mPassword; 60 61 private Spinner mEapMethod; 62 private Spinner mEapCaCert; 63 private Spinner mPhase2; 64 private Spinner mEapUserCert; 65 private TextView mEapIdentity; 66 private TextView mEapAnonymous; 67 requireKeyStore(WifiConfiguration config)68 static boolean requireKeyStore(WifiConfiguration config) { 69 String values[] = {config.ca_cert.value(), config.client_cert.value(), 70 config.private_key.value()}; 71 for (String value : values) { 72 if (value != null && value.startsWith(KEYSTORE_SPACE)) { 73 return true; 74 } 75 } 76 return false; 77 } 78 WifiDialog(Context context, DialogInterface.OnClickListener listener, AccessPoint accessPoint, boolean edit)79 WifiDialog(Context context, DialogInterface.OnClickListener listener, 80 AccessPoint accessPoint, boolean edit) { 81 super(context); 82 this.edit = edit; 83 mListener = listener; 84 mAccessPoint = accessPoint; 85 mSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE : accessPoint.security; 86 } 87 getConfig()88 WifiConfiguration getConfig() { 89 if (mAccessPoint != null && mAccessPoint.networkId != -1 && !edit) { 90 return null; 91 } 92 93 WifiConfiguration config = new WifiConfiguration(); 94 95 if (mAccessPoint == null) { 96 config.SSID = AccessPoint.convertToQuotedString( 97 mSsid.getText().toString()); 98 // If the user adds a network manually, assume that it is hidden. 99 config.hiddenSSID = true; 100 } else if (mAccessPoint.networkId == -1) { 101 config.SSID = AccessPoint.convertToQuotedString( 102 mAccessPoint.ssid); 103 } else { 104 config.networkId = mAccessPoint.networkId; 105 } 106 107 switch (mSecurity) { 108 case AccessPoint.SECURITY_NONE: 109 config.allowedKeyManagement.set(KeyMgmt.NONE); 110 return config; 111 112 case AccessPoint.SECURITY_WEP: 113 config.allowedKeyManagement.set(KeyMgmt.NONE); 114 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); 115 config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); 116 if (mPassword.length() != 0) { 117 int length = mPassword.length(); 118 String password = mPassword.getText().toString(); 119 // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) 120 if ((length == 10 || length == 26 || length == 58) && 121 password.matches("[0-9A-Fa-f]*")) { 122 config.wepKeys[0] = password; 123 } else { 124 config.wepKeys[0] = '"' + password + '"'; 125 } 126 } 127 return config; 128 129 case AccessPoint.SECURITY_PSK: 130 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 131 if (mPassword.length() != 0) { 132 String password = mPassword.getText().toString(); 133 if (password.matches("[0-9A-Fa-f]{64}")) { 134 config.preSharedKey = password; 135 } else { 136 config.preSharedKey = '"' + password + '"'; 137 } 138 } 139 return config; 140 141 case AccessPoint.SECURITY_EAP: 142 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 143 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 144 config.eap.setValue((String) mEapMethod.getSelectedItem()); 145 146 config.phase2.setValue((mPhase2.getSelectedItemPosition() == 0) ? "" : 147 "auth=" + mPhase2.getSelectedItem()); 148 config.ca_cert.setValue((mEapCaCert.getSelectedItemPosition() == 0) ? "" : 149 KEYSTORE_SPACE + Credentials.CA_CERTIFICATE + 150 (String) mEapCaCert.getSelectedItem()); 151 config.client_cert.setValue((mEapUserCert.getSelectedItemPosition() == 0) ? "" : 152 KEYSTORE_SPACE + Credentials.USER_CERTIFICATE + 153 (String) mEapUserCert.getSelectedItem()); 154 config.private_key.setValue((mEapUserCert.getSelectedItemPosition() == 0) ? "" : 155 KEYSTORE_SPACE + Credentials.USER_PRIVATE_KEY + 156 (String) mEapUserCert.getSelectedItem()); 157 config.identity.setValue((mEapIdentity.length() == 0) ? "" : 158 mEapIdentity.getText().toString()); 159 config.anonymous_identity.setValue((mEapAnonymous.length() == 0) ? "" : 160 mEapAnonymous.getText().toString()); 161 if (mPassword.length() != 0) { 162 config.password.setValue(mPassword.getText().toString()); 163 } 164 return config; 165 } 166 return null; 167 } 168 169 @Override onCreate(Bundle savedInstanceState)170 protected void onCreate(Bundle savedInstanceState) { 171 mView = getLayoutInflater().inflate(R.layout.wifi_dialog, null); 172 setView(mView); 173 setInverseBackgroundForced(true); 174 175 Context context = getContext(); 176 Resources resources = context.getResources(); 177 178 if (mAccessPoint == null) { 179 setTitle(R.string.wifi_add_network); 180 mView.findViewById(R.id.type).setVisibility(View.VISIBLE); 181 mSsid = (TextView) mView.findViewById(R.id.ssid); 182 mSsid.addTextChangedListener(this); 183 ((Spinner) mView.findViewById(R.id.security)).setOnItemSelectedListener(this); 184 setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener); 185 } else { 186 setTitle(mAccessPoint.ssid); 187 ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); 188 189 DetailedState state = mAccessPoint.getState(); 190 if (state != null) { 191 addRow(group, R.string.wifi_status, Summary.get(getContext(), state)); 192 } 193 194 String[] type = resources.getStringArray(R.array.wifi_security); 195 addRow(group, R.string.wifi_security, type[mAccessPoint.security]); 196 197 int level = mAccessPoint.getLevel(); 198 if (level != -1) { 199 String[] signal = resources.getStringArray(R.array.wifi_signal); 200 addRow(group, R.string.wifi_signal, signal[level]); 201 } 202 203 WifiInfo info = mAccessPoint.getInfo(); 204 if (info != null) { 205 addRow(group, R.string.wifi_speed, info.getLinkSpeed() + WifiInfo.LINK_SPEED_UNITS); 206 // TODO: fix the ip address for IPv6. 207 int address = info.getIpAddress(); 208 if (address != 0) { 209 addRow(group, R.string.wifi_ip_address, Formatter.formatIpAddress(address)); 210 } 211 } 212 213 if (mAccessPoint.networkId == -1 || edit) { 214 showSecurityFields(); 215 } 216 217 if (edit) { 218 setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener); 219 } else { 220 if (state == null && level != -1) { 221 setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_connect), mListener); 222 } 223 if (mAccessPoint.networkId != -1) { 224 setButton(BUTTON_FORGET, context.getString(R.string.wifi_forget), mListener); 225 } 226 } 227 } 228 229 setButton(DialogInterface.BUTTON_NEGATIVE, 230 context.getString(R.string.wifi_cancel), mListener); 231 232 super.onCreate(savedInstanceState); 233 234 if (getButton(BUTTON_SUBMIT) != null) { 235 validate(); 236 } 237 } 238 addRow(ViewGroup group, int name, String value)239 private void addRow(ViewGroup group, int name, String value) { 240 View row = getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false); 241 ((TextView) row.findViewById(R.id.name)).setText(name); 242 ((TextView) row.findViewById(R.id.value)).setText(value); 243 group.addView(row); 244 } 245 validate()246 private void validate() { 247 // TODO: make sure this is complete. 248 if ((mSsid != null && mSsid.length() == 0) || 249 ((mAccessPoint == null || mAccessPoint.networkId == -1) && 250 ((mSecurity == AccessPoint.SECURITY_WEP && mPassword.length() == 0) || 251 (mSecurity == AccessPoint.SECURITY_PSK && mPassword.length() < 8)))) { 252 getButton(BUTTON_SUBMIT).setEnabled(false); 253 } else { 254 getButton(BUTTON_SUBMIT).setEnabled(true); 255 } 256 } 257 onClick(View view)258 public void onClick(View view) { 259 mPassword.setInputType( 260 InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ? 261 InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : 262 InputType.TYPE_TEXT_VARIATION_PASSWORD)); 263 } 264 onTextChanged(CharSequence s, int start, int before, int count)265 public void onTextChanged(CharSequence s, int start, int before, int count) { 266 } 267 beforeTextChanged(CharSequence s, int start, int count, int after)268 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 269 } 270 afterTextChanged(Editable editable)271 public void afterTextChanged(Editable editable) { 272 validate(); 273 } 274 onItemSelected(AdapterView parent, View view, int position, long id)275 public void onItemSelected(AdapterView parent, View view, int position, long id) { 276 mSecurity = position; 277 showSecurityFields(); 278 validate(); 279 } 280 onNothingSelected(AdapterView parent)281 public void onNothingSelected(AdapterView parent) { 282 } 283 showSecurityFields()284 private void showSecurityFields() { 285 if (mSecurity == AccessPoint.SECURITY_NONE) { 286 mView.findViewById(R.id.fields).setVisibility(View.GONE); 287 return; 288 } 289 mView.findViewById(R.id.fields).setVisibility(View.VISIBLE); 290 291 if (mPassword == null) { 292 mPassword = (TextView) mView.findViewById(R.id.password); 293 mPassword.addTextChangedListener(this); 294 ((CheckBox) mView.findViewById(R.id.show_password)).setOnClickListener(this); 295 296 if (mAccessPoint != null && mAccessPoint.networkId != -1) { 297 mPassword.setHint(R.string.wifi_unchanged); 298 } 299 } 300 301 if (mSecurity != AccessPoint.SECURITY_EAP) { 302 mView.findViewById(R.id.eap).setVisibility(View.GONE); 303 return; 304 } 305 mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); 306 307 if (mEapMethod == null) { 308 mEapMethod = (Spinner) mView.findViewById(R.id.method); 309 mPhase2 = (Spinner) mView.findViewById(R.id.phase2); 310 mEapCaCert = (Spinner) mView.findViewById(R.id.ca_cert); 311 mEapUserCert = (Spinner) mView.findViewById(R.id.user_cert); 312 mEapIdentity = (TextView) mView.findViewById(R.id.identity); 313 mEapAnonymous = (TextView) mView.findViewById(R.id.anonymous); 314 315 loadCertificates(mEapCaCert, Credentials.CA_CERTIFICATE); 316 loadCertificates(mEapUserCert, Credentials.USER_PRIVATE_KEY); 317 318 if (mAccessPoint != null && mAccessPoint.networkId != -1) { 319 WifiConfiguration config = mAccessPoint.getConfig(); 320 setSelection(mEapMethod, config.eap.value()); 321 setSelection(mPhase2, config.phase2.value()); 322 setCertificate(mEapCaCert, Credentials.CA_CERTIFICATE, 323 config.ca_cert.value()); 324 setCertificate(mEapUserCert, Credentials.USER_PRIVATE_KEY, 325 config.private_key.value()); 326 mEapIdentity.setText(config.identity.value()); 327 mEapAnonymous.setText(config.anonymous_identity.value()); 328 } 329 } 330 } 331 loadCertificates(Spinner spinner, String prefix)332 private void loadCertificates(Spinner spinner, String prefix) { 333 String[] certs = KeyStore.getInstance().saw(prefix); 334 Context context = getContext(); 335 String unspecified = context.getString(R.string.wifi_unspecified); 336 337 if (certs == null || certs.length == 0) { 338 certs = new String[] {unspecified}; 339 } else { 340 String[] array = new String[certs.length + 1]; 341 array[0] = unspecified; 342 System.arraycopy(certs, 0, array, 1, certs.length); 343 certs = array; 344 } 345 346 ArrayAdapter<String> adapter = new ArrayAdapter<String>( 347 context, android.R.layout.simple_spinner_item, certs); 348 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 349 spinner.setAdapter(adapter); 350 } 351 setCertificate(Spinner spinner, String prefix, String cert)352 private void setCertificate(Spinner spinner, String prefix, String cert) { 353 prefix = KEYSTORE_SPACE + prefix; 354 if (cert != null && cert.startsWith(prefix)) { 355 setSelection(spinner, cert.substring(prefix.length())); 356 } 357 } 358 setSelection(Spinner spinner, String value)359 private void setSelection(Spinner spinner, String value) { 360 if (value != null) { 361 ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter(); 362 for (int i = adapter.getCount() - 1; i >= 0; --i) { 363 if (value.equals(adapter.getItem(i))) { 364 spinner.setSelection(i); 365 break; 366 } 367 } 368 } 369 } 370 } 371