WifiSettings.java revision 5536c4a052ed307028394fcd2cafe141d723e54b
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 com.android.settings.ProgressCategory; 20import com.android.settings.R; 21 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.DialogInterface; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.net.ConnectivityManager; 28import android.net.NetworkInfo; 29import android.net.NetworkInfo.DetailedState; 30import android.net.wifi.ScanResult; 31import android.net.wifi.SupplicantState; 32import android.net.wifi.WifiConfiguration; 33import android.net.wifi.WifiConfiguration.KeyMgmt; 34import android.net.wifi.WifiConfiguration.Status; 35import android.net.wifi.WifiInfo; 36import android.net.wifi.WifiManager; 37import android.os.Bundle; 38import android.os.Handler; 39import android.os.Message; 40import android.preference.CheckBoxPreference; 41import android.preference.Preference; 42import android.preference.PreferenceActivity; 43import android.preference.PreferenceScreen; 44import android.provider.Settings.Secure; 45import android.security.Credentials; 46import android.security.KeyStore; 47import android.text.TextUtils; 48import android.view.ContextMenu; 49import android.view.ContextMenu.ContextMenuInfo; 50import android.view.Menu; 51import android.view.MenuItem; 52import android.view.View; 53import android.widget.AdapterView.AdapterContextMenuInfo; 54import android.widget.Toast; 55 56import java.util.ArrayList; 57import java.util.List; 58 59public class WifiSettings extends PreferenceActivity implements DialogInterface.OnClickListener { 60 private static final int MENU_ID_SCAN = Menu.FIRST; 61 private static final int MENU_ID_ADVANCED = Menu.FIRST + 1; 62 private static final int MENU_ID_CONNECT = Menu.FIRST + 2; 63 private static final int MENU_ID_FORGET = Menu.FIRST + 3; 64 private static final int MENU_ID_MODIFY = Menu.FIRST + 4; 65 66 // this boolean extra specifies whether to disable the Next button when not connected 67 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 68 69 private final IntentFilter mFilter; 70 private final BroadcastReceiver mReceiver; 71 private final Scanner mScanner; 72 73 private WifiManager mWifiManager; 74 private WifiEnabler mWifiEnabler; 75 private CheckBoxPreference mNotifyOpenNetworks; 76 private ProgressCategory mAccessPoints; 77 private Preference mAddNetwork; 78 79 private DetailedState mLastState; 80 private WifiInfo mLastInfo; 81 private int mLastPriority; 82 83 private boolean mResetNetworks = false; 84 private int mKeyStoreNetworkId = -1; 85 86 private AccessPoint mSelected; 87 private WifiDialog mDialog; 88 89 // should Next button only be enabled when we have a connection? 90 private boolean mEnableNextOnConnection; 91 92 public WifiSettings() { 93 mFilter = new IntentFilter(); 94 mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 95 mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 96 mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 97 mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 98 mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 99 mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 100 101 mReceiver = new BroadcastReceiver() { 102 @Override 103 public void onReceive(Context context, Intent intent) { 104 handleEvent(intent); 105 } 106 }; 107 108 mScanner = new Scanner(); 109 } 110 111 @Override 112 protected void onCreate(Bundle savedInstanceState) { 113 super.onCreate(savedInstanceState); 114 115 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 116 117 // if we're supposed to enable/disable the Next button based on our current connection 118 // state, start it off in the right state 119 mEnableNextOnConnection = getIntent().getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 120 if (mEnableNextOnConnection && hasNextButton()) { 121 ConnectivityManager connectivity = (ConnectivityManager) 122 getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); 123 if (connectivity != null) { 124 NetworkInfo info = connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 125 getNextButton().setEnabled(info.isConnected()); 126 } 127 } 128 129 if (getIntent().getBooleanExtra("only_access_points", false)) { 130 addPreferencesFromResource(R.xml.wifi_access_points); 131 } else { 132 addPreferencesFromResource(R.xml.wifi_settings); 133 mWifiEnabler = new WifiEnabler(this, 134 (CheckBoxPreference) findPreference("enable_wifi")); 135 mNotifyOpenNetworks = 136 (CheckBoxPreference) findPreference("notify_open_networks"); 137 mNotifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(), 138 Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1); 139 } 140 141 mAccessPoints = (ProgressCategory) findPreference("access_points"); 142 mAccessPoints.setOrderingAsAdded(false); 143 mAddNetwork = findPreference("add_network"); 144 145 registerForContextMenu(getListView()); 146 } 147 148 @Override 149 protected void onResume() { 150 super.onResume(); 151 if (mWifiEnabler != null) { 152 mWifiEnabler.resume(); 153 } 154 registerReceiver(mReceiver, mFilter); 155 if (mKeyStoreNetworkId != -1 && KeyStore.getInstance().test() == KeyStore.NO_ERROR) { 156 connect(mKeyStoreNetworkId); 157 } 158 mKeyStoreNetworkId = -1; 159 } 160 161 @Override 162 protected void onPause() { 163 super.onPause(); 164 if (mWifiEnabler != null) { 165 mWifiEnabler.pause(); 166 } 167 unregisterReceiver(mReceiver); 168 mScanner.pause(); 169 if (mDialog != null) { 170 mDialog.dismiss(); 171 mDialog = null; 172 } 173 if (mResetNetworks) { 174 enableNetworks(); 175 } 176 } 177 178 @Override 179 public boolean onCreateOptionsMenu(Menu menu) { 180 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) 181 .setIcon(R.drawable.ic_menu_scan_network); 182 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 183 .setIcon(android.R.drawable.ic_menu_manage); 184 return super.onCreateOptionsMenu(menu); 185 } 186 187 @Override 188 public boolean onOptionsItemSelected(MenuItem item) { 189 switch (item.getItemId()) { 190 case MENU_ID_SCAN: 191 if (mWifiManager.isWifiEnabled()) { 192 mScanner.resume(); 193 } 194 return true; 195 case MENU_ID_ADVANCED: 196 startActivity(new Intent(this, AdvancedSettings.class)); 197 return true; 198 } 199 return super.onOptionsItemSelected(item); 200 } 201 202 @Override 203 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 204 if (info instanceof AdapterContextMenuInfo) { 205 Preference preference = (Preference) getListView().getItemAtPosition( 206 ((AdapterContextMenuInfo) info).position); 207 208 if (preference instanceof AccessPoint) { 209 mSelected = (AccessPoint) preference; 210 menu.setHeaderTitle(mSelected.ssid); 211 if (mSelected.getLevel() != -1 && mSelected.getState() == null) { 212 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 213 } 214 if (mSelected.networkId != -1) { 215 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 216 if (mSelected.security != AccessPoint.SECURITY_NONE) { 217 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 218 } 219 } 220 } 221 } 222 } 223 224 @Override 225 public boolean onContextItemSelected(MenuItem item) { 226 if (mSelected == null) { 227 return super.onContextItemSelected(item); 228 } 229 switch (item.getItemId()) { 230 case MENU_ID_CONNECT: 231 if (mSelected.networkId != -1) { 232 if (!requireKeyStore(mSelected.getConfig())) { 233 connect(mSelected.networkId); 234 } 235 } else if (mSelected.security == AccessPoint.SECURITY_NONE) { 236 // Shortcut for open networks. 237 WifiConfiguration config = new WifiConfiguration(); 238 config.SSID = mSelected.ssid; 239 config.allowedKeyManagement.set(KeyMgmt.NONE); 240 int networkId = mWifiManager.addNetwork(config); 241 mWifiManager.enableNetwork(networkId, false); 242 connect(networkId); 243 } else { 244 showDialog(mSelected, false); 245 } 246 return true; 247 case MENU_ID_FORGET: 248 forget(mSelected.networkId); 249 return true; 250 case MENU_ID_MODIFY: 251 showDialog(mSelected, true); 252 return true; 253 } 254 return super.onContextItemSelected(item); 255 } 256 257 @Override 258 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 259 if (preference instanceof AccessPoint) { 260 mSelected = (AccessPoint) preference; 261 showDialog(mSelected, false); 262 } else if (preference == mAddNetwork) { 263 mSelected = null; 264 showDialog(null, true); 265 } else if (preference == mNotifyOpenNetworks) { 266 Secure.putInt(getContentResolver(), 267 Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 268 mNotifyOpenNetworks.isChecked() ? 1 : 0); 269 } else { 270 return super.onPreferenceTreeClick(screen, preference); 271 } 272 return true; 273 } 274 275 public void onClick(DialogInterface dialogInterface, int button) { 276 if (button == WifiDialog.BUTTON_FORGET && mSelected != null) { 277 forget(mSelected.networkId); 278 } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) { 279 WifiConfiguration config = mDialog.getConfig(); 280 281 if (config == null) { 282 if (mSelected != null && !requireKeyStore(mSelected.getConfig())) { 283 connect(mSelected.networkId); 284 } 285 } else if (config.networkId != -1) { 286 if (mSelected != null) { 287 mWifiManager.updateNetwork(config); 288 saveNetworks(); 289 } 290 } else { 291 int networkId = mWifiManager.addNetwork(config); 292 if (networkId != -1) { 293 mWifiManager.enableNetwork(networkId, false); 294 config.networkId = networkId; 295 if (mDialog.edit || requireKeyStore(config)) { 296 saveNetworks(); 297 } else { 298 connect(networkId); 299 } 300 } 301 } 302 } 303 } 304 305 private void showDialog(AccessPoint accessPoint, boolean edit) { 306 if (mDialog != null) { 307 mDialog.dismiss(); 308 } 309 mDialog = new WifiDialog(this, this, accessPoint, edit); 310 mDialog.show(); 311 } 312 313 private boolean requireKeyStore(WifiConfiguration config) { 314 if (WifiDialog.requireKeyStore(config) && 315 KeyStore.getInstance().test() != KeyStore.NO_ERROR) { 316 mKeyStoreNetworkId = config.networkId; 317 Credentials.getInstance().unlock(this); 318 return true; 319 } 320 return false; 321 } 322 323 private void forget(int networkId) { 324 mWifiManager.removeNetwork(networkId); 325 saveNetworks(); 326 } 327 328 private void connect(int networkId) { 329 if (networkId == -1) { 330 return; 331 } 332 333 // Reset the priority of each network if it goes too high. 334 if (mLastPriority > 1000000) { 335 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 336 AccessPoint accessPoint = (AccessPoint) mAccessPoints.getPreference(i); 337 if (accessPoint.networkId != -1) { 338 WifiConfiguration config = new WifiConfiguration(); 339 config.networkId = accessPoint.networkId; 340 config.priority = 0; 341 mWifiManager.updateNetwork(config); 342 } 343 } 344 mLastPriority = 0; 345 } 346 347 // Set to the highest priority and save the configuration. 348 WifiConfiguration config = new WifiConfiguration(); 349 config.networkId = networkId; 350 config.priority = ++mLastPriority; 351 mWifiManager.updateNetwork(config); 352 saveNetworks(); 353 354 // Connect to network by disabling others. 355 mWifiManager.enableNetwork(networkId, true); 356 mWifiManager.reconnect(); 357 mResetNetworks = true; 358 } 359 360 private void enableNetworks() { 361 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 362 WifiConfiguration config = ((AccessPoint) mAccessPoints.getPreference(i)).getConfig(); 363 if (config != null && config.status != Status.ENABLED) { 364 mWifiManager.enableNetwork(config.networkId, false); 365 } 366 } 367 mResetNetworks = false; 368 } 369 370 private void saveNetworks() { 371 // Always save the configuration with all networks enabled. 372 enableNetworks(); 373 mWifiManager.saveConfiguration(); 374 updateAccessPoints(); 375 } 376 377 private void updateAccessPoints() { 378 List<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 379 380 List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 381 if (configs != null) { 382 mLastPriority = 0; 383 for (WifiConfiguration config : configs) { 384 if (config.priority > mLastPriority) { 385 mLastPriority = config.priority; 386 } 387 388 // Shift the status to make enableNetworks() more efficient. 389 if (config.status == Status.CURRENT) { 390 config.status = Status.ENABLED; 391 } else if (mResetNetworks && config.status == Status.DISABLED) { 392 config.status = Status.CURRENT; 393 } 394 395 AccessPoint accessPoint = new AccessPoint(this, config); 396 accessPoint.update(mLastInfo, mLastState); 397 accessPoints.add(accessPoint); 398 } 399 } 400 401 List<ScanResult> results = mWifiManager.getScanResults(); 402 if (results != null) { 403 for (ScanResult result : results) { 404 // Ignore hidden and ad-hoc networks. 405 if (result.SSID == null || result.SSID.length() == 0 || 406 result.capabilities.contains("[IBSS]")) { 407 continue; 408 } 409 410 boolean found = false; 411 for (AccessPoint accessPoint : accessPoints) { 412 if (accessPoint.update(result)) { 413 found = true; 414 } 415 } 416 if (!found) { 417 accessPoints.add(new AccessPoint(this, result)); 418 } 419 } 420 } 421 422 mAccessPoints.removeAll(); 423 for (AccessPoint accessPoint : accessPoints) { 424 mAccessPoints.addPreference(accessPoint); 425 } 426 } 427 428 private void handleEvent(Intent intent) { 429 String action = intent.getAction(); 430 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 431 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 432 WifiManager.WIFI_STATE_UNKNOWN)); 433 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { 434 updateAccessPoints(); 435 } else if (WifiManager.NETWORK_IDS_CHANGED_ACTION.equals(action)) { 436 if (mSelected != null && mSelected.networkId != -1) { 437 mSelected = null; 438 } 439 updateAccessPoints(); 440 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 441 updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState) 442 intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE))); 443 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 444 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 445 WifiManager.EXTRA_NETWORK_INFO); 446 if (mEnableNextOnConnection && hasNextButton()) { 447 getNextButton().setEnabled(info.isConnected()); 448 } 449 updateConnectionState(info.getDetailedState()); 450 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 451 updateConnectionState(null); 452 } 453 } 454 455 private void updateConnectionState(DetailedState state) { 456 /* sticky broadcasts can call this when wifi is disabled */ 457 if (!mWifiManager.isWifiEnabled()) 458 return; 459 460 if (state == DetailedState.OBTAINING_IPADDR) { 461 mScanner.pause(); 462 } else { 463 mScanner.resume(); 464 } 465 466 mLastInfo = mWifiManager.getConnectionInfo(); 467 if (state != null) { 468 mLastState = state; 469 } 470 471 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 472 ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState); 473 } 474 475 if (mResetNetworks && (state == DetailedState.CONNECTED || 476 state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) { 477 updateAccessPoints(); 478 enableNetworks(); 479 } 480 } 481 482 private void updateWifiState(int state) { 483 if (state == WifiManager.WIFI_STATE_ENABLED) { 484 mScanner.resume(); 485 updateAccessPoints(); 486 } else { 487 mScanner.pause(); 488 mAccessPoints.removeAll(); 489 } 490 } 491 492 private class Scanner extends Handler { 493 private int mRetry = 0; 494 495 void resume() { 496 if (!hasMessages(0)) { 497 sendEmptyMessage(0); 498 } 499 } 500 501 void pause() { 502 mRetry = 0; 503 mAccessPoints.setProgress(false); 504 removeMessages(0); 505 } 506 507 @Override 508 public void handleMessage(Message message) { 509 if (mWifiManager.startScanActive()) { 510 mRetry = 0; 511 } else if (++mRetry >= 3) { 512 mRetry = 0; 513 Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan, 514 Toast.LENGTH_LONG).show(); 515 } 516 mAccessPoints.setProgress(mRetry != 0); 517 sendEmptyMessageDelayed(0, 6000); 518 } 519 } 520} 521