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