WifiSettings.java revision f5a6e5bfa9497e41d518c24d1b5f3b235962a379
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 mScanner.resume(); 192 return true; 193 case MENU_ID_ADVANCED: 194 startActivity(new Intent(this, AdvancedSettings.class)); 195 return true; 196 } 197 return super.onOptionsItemSelected(item); 198 } 199 200 @Override 201 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 202 if (info instanceof AdapterContextMenuInfo) { 203 Preference preference = (Preference) getListView().getItemAtPosition( 204 ((AdapterContextMenuInfo) info).position); 205 206 if (preference instanceof AccessPoint) { 207 mSelected = (AccessPoint) preference; 208 menu.setHeaderTitle(mSelected.ssid); 209 if (mSelected.getLevel() != -1 && mSelected.getState() == null) { 210 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 211 } 212 if (mSelected.networkId != -1) { 213 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 214 if (mSelected.security != AccessPoint.SECURITY_NONE) { 215 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 216 } 217 } 218 } 219 } 220 } 221 222 @Override 223 public boolean onContextItemSelected(MenuItem item) { 224 if (mSelected == null) { 225 return super.onContextItemSelected(item); 226 } 227 switch (item.getItemId()) { 228 case MENU_ID_CONNECT: 229 if (mSelected.networkId != -1) { 230 if (!requireKeyStore(mSelected.getConfig())) { 231 connect(mSelected.networkId); 232 } 233 } else if (mSelected.security == AccessPoint.SECURITY_NONE) { 234 // Shortcut for open networks. 235 WifiConfiguration config = new WifiConfiguration(); 236 config.SSID = mSelected.ssid; 237 config.allowedKeyManagement.set(KeyMgmt.NONE); 238 int networkId = mWifiManager.addNetwork(config); 239 mWifiManager.enableNetwork(networkId, false); 240 connect(networkId); 241 } else { 242 showDialog(mSelected, false); 243 } 244 return true; 245 case MENU_ID_FORGET: 246 forget(mSelected.networkId); 247 return true; 248 case MENU_ID_MODIFY: 249 showDialog(mSelected, true); 250 return true; 251 } 252 return super.onContextItemSelected(item); 253 } 254 255 @Override 256 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 257 if (preference instanceof AccessPoint) { 258 mSelected = (AccessPoint) preference; 259 showDialog(mSelected, false); 260 } else if (preference == mAddNetwork) { 261 mSelected = null; 262 showDialog(null, true); 263 } else if (preference == mNotifyOpenNetworks) { 264 Secure.putInt(getContentResolver(), 265 Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 266 mNotifyOpenNetworks.isChecked() ? 1 : 0); 267 } else { 268 return super.onPreferenceTreeClick(screen, preference); 269 } 270 return true; 271 } 272 273 public void onClick(DialogInterface dialogInterface, int button) { 274 if (button == WifiDialog.BUTTON_FORGET && mSelected != null) { 275 forget(mSelected.networkId); 276 } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) { 277 WifiConfiguration config = mDialog.getConfig(); 278 279 if (config == null) { 280 if (mSelected != null && !requireKeyStore(mSelected.getConfig())) { 281 connect(mSelected.networkId); 282 } 283 } else if (config.networkId != -1) { 284 if (mSelected != null) { 285 mWifiManager.updateNetwork(config); 286 saveNetworks(); 287 } 288 } else { 289 int networkId = mWifiManager.addNetwork(config); 290 if (networkId != -1) { 291 mWifiManager.enableNetwork(networkId, false); 292 config.networkId = networkId; 293 if (mDialog.edit || requireKeyStore(config)) { 294 saveNetworks(); 295 } else { 296 connect(networkId); 297 } 298 } 299 } 300 } 301 } 302 303 private void showDialog(AccessPoint accessPoint, boolean edit) { 304 if (mDialog != null) { 305 mDialog.dismiss(); 306 } 307 mDialog = new WifiDialog(this, this, accessPoint, edit); 308 mDialog.show(); 309 } 310 311 private boolean requireKeyStore(WifiConfiguration config) { 312 if (WifiDialog.requireKeyStore(config) && 313 KeyStore.getInstance().test() != KeyStore.NO_ERROR) { 314 mKeyStoreNetworkId = config.networkId; 315 Credentials.getInstance().unlock(this); 316 return true; 317 } 318 return false; 319 } 320 321 private void forget(int networkId) { 322 mWifiManager.removeNetwork(networkId); 323 saveNetworks(); 324 } 325 326 private void connect(int networkId) { 327 if (networkId == -1) { 328 return; 329 } 330 331 // Reset the priority of each network if it goes too high. 332 if (mLastPriority > 1000000) { 333 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 334 AccessPoint accessPoint = (AccessPoint) mAccessPoints.getPreference(i); 335 if (accessPoint.networkId != -1) { 336 WifiConfiguration config = new WifiConfiguration(); 337 config.networkId = accessPoint.networkId; 338 config.priority = 0; 339 mWifiManager.updateNetwork(config); 340 } 341 } 342 mLastPriority = 0; 343 } 344 345 // Set to the highest priority and save the configuration. 346 WifiConfiguration config = new WifiConfiguration(); 347 config.networkId = networkId; 348 config.priority = ++mLastPriority; 349 mWifiManager.updateNetwork(config); 350 saveNetworks(); 351 352 // Connect to network by disabling others. 353 mWifiManager.enableNetwork(networkId, true); 354 mWifiManager.reconnect(); 355 mResetNetworks = true; 356 } 357 358 private void enableNetworks() { 359 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 360 WifiConfiguration config = ((AccessPoint) mAccessPoints.getPreference(i)).getConfig(); 361 if (config != null && config.status != Status.ENABLED) { 362 mWifiManager.enableNetwork(config.networkId, false); 363 } 364 } 365 mResetNetworks = false; 366 } 367 368 private void saveNetworks() { 369 // Always save the configuration with all networks enabled. 370 enableNetworks(); 371 mWifiManager.saveConfiguration(); 372 updateAccessPoints(); 373 } 374 375 private void updateAccessPoints() { 376 List<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 377 378 List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 379 if (configs != null) { 380 mLastPriority = 0; 381 for (WifiConfiguration config : configs) { 382 if (config.priority > mLastPriority) { 383 mLastPriority = config.priority; 384 } 385 386 // Shift the status to make enableNetworks() more efficient. 387 if (config.status == Status.CURRENT) { 388 config.status = Status.ENABLED; 389 } else if (mResetNetworks && config.status == Status.DISABLED) { 390 config.status = Status.CURRENT; 391 } 392 393 AccessPoint accessPoint = new AccessPoint(this, config); 394 accessPoint.update(mLastInfo, mLastState); 395 accessPoints.add(accessPoint); 396 } 397 } 398 399 List<ScanResult> results = mWifiManager.getScanResults(); 400 if (results != null) { 401 for (ScanResult result : results) { 402 // Ignore hidden and ad-hoc networks. 403 if (result.SSID == null || result.SSID.length() == 0 || 404 result.capabilities.contains("[IBSS]")) { 405 continue; 406 } 407 408 boolean found = false; 409 for (AccessPoint accessPoint : accessPoints) { 410 if (accessPoint.update(result)) { 411 found = true; 412 } 413 } 414 if (!found) { 415 accessPoints.add(new AccessPoint(this, result)); 416 } 417 } 418 } 419 420 mAccessPoints.removeAll(); 421 for (AccessPoint accessPoint : accessPoints) { 422 mAccessPoints.addPreference(accessPoint); 423 } 424 } 425 426 private void handleEvent(Intent intent) { 427 String action = intent.getAction(); 428 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 429 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 430 WifiManager.WIFI_STATE_UNKNOWN)); 431 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { 432 updateAccessPoints(); 433 } else if (WifiManager.NETWORK_IDS_CHANGED_ACTION.equals(action)) { 434 if (mSelected != null && mSelected.networkId != -1) { 435 mSelected = null; 436 } 437 updateAccessPoints(); 438 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 439 updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState) 440 intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE))); 441 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 442 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 443 WifiManager.EXTRA_NETWORK_INFO); 444 if (mEnableNextOnConnection && hasNextButton()) { 445 getNextButton().setEnabled(info.isConnected()); 446 } 447 updateConnectionState(info.getDetailedState()); 448 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 449 updateConnectionState(null); 450 } 451 } 452 453 private void updateConnectionState(DetailedState state) { 454 if (state == DetailedState.OBTAINING_IPADDR) { 455 mScanner.pause(); 456 } else { 457 mScanner.resume(); 458 } 459 460 mLastInfo = mWifiManager.getConnectionInfo(); 461 if (state != null) { 462 mLastState = state; 463 } 464 465 for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { 466 ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState); 467 } 468 469 if (mResetNetworks && (state == DetailedState.CONNECTED || 470 state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) { 471 updateAccessPoints(); 472 enableNetworks(); 473 } 474 } 475 476 private void updateWifiState(int state) { 477 if (state == WifiManager.WIFI_STATE_ENABLED) { 478 mScanner.resume(); 479 updateAccessPoints(); 480 } else { 481 mScanner.pause(); 482 mAccessPoints.removeAll(); 483 } 484 } 485 486 private class Scanner extends Handler { 487 private int mRetry = 0; 488 489 void resume() { 490 if (!hasMessages(0)) { 491 sendEmptyMessage(0); 492 } 493 } 494 495 void pause() { 496 mRetry = 0; 497 mAccessPoints.setProgress(false); 498 removeMessages(0); 499 } 500 501 @Override 502 public void handleMessage(Message message) { 503 if (mWifiManager.startScanActive()) { 504 mRetry = 0; 505 } else if (++mRetry >= 3) { 506 mRetry = 0; 507 Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan, 508 Toast.LENGTH_LONG).show(); 509 } 510 mAccessPoints.setProgress(mRetry != 0); 511 sendEmptyMessageDelayed(0, 6000); 512 } 513 } 514} 515