WifiSettings.java revision 3ab5c6838f01cd6306c0ca2728f3c01e29a8318f
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 static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 20 21import android.app.ActionBar; 22import android.app.Activity; 23import android.app.AlertDialog; 24import android.app.Dialog; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.net.ConnectivityManager; 31import android.net.NetworkInfo; 32import android.net.NetworkInfo.DetailedState; 33import android.net.wifi.ScanResult; 34import android.net.wifi.SupplicantState; 35import android.net.wifi.WifiConfiguration; 36import android.net.wifi.WifiConfiguration.KeyMgmt; 37import android.net.wifi.WifiInfo; 38import android.net.wifi.WifiManager; 39import android.net.wifi.WpsInfo; 40import android.os.Bundle; 41import android.os.Handler; 42import android.os.Message; 43import android.preference.Preference; 44import android.preference.PreferenceActivity; 45import android.preference.PreferenceScreen; 46import android.security.Credentials; 47import android.security.KeyStore; 48import android.util.Log; 49import android.view.ContextMenu; 50import android.view.ContextMenu.ContextMenuInfo; 51import android.view.Gravity; 52import android.view.Menu; 53import android.view.MenuInflater; 54import android.view.MenuItem; 55import android.view.View; 56import android.widget.AdapterView.AdapterContextMenuInfo; 57import android.widget.Switch; 58import android.widget.TextView; 59import android.widget.Toast; 60 61import com.android.settings.R; 62import com.android.settings.SettingsPreferenceFragment; 63import com.android.settings.wifi.p2p.WifiP2pSettings; 64 65import java.util.ArrayList; 66import java.util.Collection; 67import java.util.Collections; 68import java.util.HashMap; 69import java.util.List; 70import java.util.concurrent.atomic.AtomicBoolean; 71 72/** 73 * Two types of UI are provided here. 74 * 75 * The first is for "usual Settings", appearing as any other Setup fragment. 76 * 77 * The second is for Setup Wizard, with a simplified interface that hides the action bar 78 * and menus. 79 */ 80public class WifiSettings extends SettingsPreferenceFragment 81 implements DialogInterface.OnClickListener { 82 private static final String TAG = "WifiSettings"; 83 private static final int MENU_ID_WPS_PBC = Menu.FIRST; 84 private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; 85 private static final int MENU_ID_P2P = Menu.FIRST + 2; 86 private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3; 87 private static final int MENU_ID_ADVANCED = Menu.FIRST + 4; 88 private static final int MENU_ID_SCAN = Menu.FIRST + 5; 89 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 90 private static final int MENU_ID_FORGET = Menu.FIRST + 7; 91 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 92 93 private static final int WIFI_DIALOG_ID = 1; 94 private static final int WPS_PBC_DIALOG_ID = 2; 95 private static final int WPS_PIN_DIALOG_ID = 3; 96 97 // Combo scans can take 5-6s to complete - set to 10s. 98 private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; 99 100 // Instance state keys 101 private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode"; 102 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 103 104 private final IntentFilter mFilter; 105 private final BroadcastReceiver mReceiver; 106 private final Scanner mScanner; 107 108 private WifiManager mWifiManager; 109 private WifiManager.Channel mChannel; 110 private WifiManager.ActionListener mConnectListener; 111 private WifiManager.ActionListener mSaveListener; 112 private WifiManager.ActionListener mForgetListener; 113 114 115 private WifiEnabler mWifiEnabler; 116 // An access point being editted is stored here. 117 private AccessPoint mSelectedAccessPoint; 118 119 private DetailedState mLastState; 120 private WifiInfo mLastInfo; 121 122 private AtomicBoolean mConnected = new AtomicBoolean(false); 123 124 private int mKeyStoreNetworkId = INVALID_NETWORK_ID; 125 126 private WifiDialog mDialog; 127 128 private TextView mEmptyView; 129 130 /* Used in Wifi Setup context */ 131 132 // this boolean extra specifies whether to disable the Next button when not connected 133 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 134 135 private static final String EXTRA_WIFI_SHOW_ACTION_BAR = "wifi_show_action_bar"; 136 private static final String EXTRA_WIFI_SHOW_MENUS = "wifi_show_menus"; 137 138 // should Next button only be enabled when we have a connection? 139 private boolean mEnableNextOnConnection; 140 141 // Save the dialog details 142 private boolean mDlgEdit; 143 private AccessPoint mDlgAccessPoint; 144 private Bundle mAccessPointSavedState; 145 146 // Menus are not shown by Setup Wizard 147 private boolean mShowMenu; 148 149 /* End of "used in Wifi Setup context" */ 150 151 public WifiSettings() { 152 mFilter = new IntentFilter(); 153 mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 154 mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 155 mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 156 mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 157 mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 158 mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 159 mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 160 mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 161 162 mReceiver = new BroadcastReceiver() { 163 @Override 164 public void onReceive(Context context, Intent intent) { 165 handleEvent(context, intent); 166 } 167 }; 168 169 mScanner = new Scanner(); 170 } 171 172 @Override 173 public void onActivityCreated(Bundle savedInstanceState) { 174 // We don't call super.onActivityCreated() here, since it assumes we already set up 175 // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in 176 // this method. 177 178 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 179 mChannel = mWifiManager.initialize(getActivity(), getActivity().getMainLooper(), null); 180 181 mConnectListener = new WifiManager.ActionListener() { 182 public void onSuccess() { 183 } 184 public void onFailure(int reason) { 185 Toast.makeText(getActivity(), 186 R.string.wifi_failed_connect_message, 187 Toast.LENGTH_SHORT).show(); 188 } 189 }; 190 191 mSaveListener = new WifiManager.ActionListener() { 192 public void onSuccess() { 193 } 194 public void onFailure(int reason) { 195 Toast.makeText(getActivity(), 196 R.string.wifi_failed_save_message, 197 Toast.LENGTH_SHORT).show(); 198 } 199 }; 200 201 mForgetListener = new WifiManager.ActionListener() { 202 public void onSuccess() { 203 } 204 public void onFailure(int reason) { 205 Toast.makeText(getActivity(), 206 R.string.wifi_failed_forget_message, 207 Toast.LENGTH_SHORT).show(); 208 } 209 }; 210 211 if (savedInstanceState != null 212 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 213 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); 214 mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 215 } 216 217 final Activity activity = getActivity(); 218 final Intent intent = activity.getIntent(); 219 220 // if we're supposed to enable/disable the Next button based on our current connection 221 // state, start it off in the right state 222 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 223 224 if (mEnableNextOnConnection) { 225 if (hasNextButton()) { 226 final ConnectivityManager connectivity = (ConnectivityManager) 227 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 228 if (connectivity != null) { 229 NetworkInfo info = connectivity.getNetworkInfo( 230 ConnectivityManager.TYPE_WIFI); 231 changeNextButtonState(info.isConnected()); 232 } 233 } 234 } 235 236 addPreferencesFromResource(R.xml.wifi_settings); 237 238 // Action bar is hidden for Setup Wizard 239 final boolean showActionBar = intent.getBooleanExtra(EXTRA_WIFI_SHOW_ACTION_BAR, true); 240 if (showActionBar) { 241 Switch actionBarSwitch = new Switch(activity); 242 243 if (activity instanceof PreferenceActivity) { 244 PreferenceActivity preferenceActivity = (PreferenceActivity) activity; 245 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { 246 final int padding = activity.getResources().getDimensionPixelSize( 247 R.dimen.action_bar_switch_padding); 248 actionBarSwitch.setPadding(0, 0, padding, 0); 249 activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 250 ActionBar.DISPLAY_SHOW_CUSTOM); 251 activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( 252 ActionBar.LayoutParams.WRAP_CONTENT, 253 ActionBar.LayoutParams.WRAP_CONTENT, 254 Gravity.CENTER_VERTICAL | Gravity.RIGHT)); 255 } 256 } 257 258 mWifiEnabler = new WifiEnabler(activity, actionBarSwitch); 259 } 260 261 mEmptyView = (TextView) getView().findViewById(android.R.id.empty); 262 getListView().setEmptyView(mEmptyView); 263 264 // Menu is hidden for Setup Wizard 265 mShowMenu = intent.getBooleanExtra(EXTRA_WIFI_SHOW_MENUS, true); 266 if (mShowMenu) { 267 registerForContextMenu(getListView()); 268 } 269 setHasOptionsMenu(mShowMenu); 270 271 // After confirming PreferenceScreen is available, we call super. 272 super.onActivityCreated(savedInstanceState); 273 } 274 275 @Override 276 public void onResume() { 277 super.onResume(); 278 if (mWifiEnabler != null) { 279 mWifiEnabler.resume(); 280 } 281 282 getActivity().registerReceiver(mReceiver, mFilter); 283 if (mKeyStoreNetworkId != INVALID_NETWORK_ID && 284 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) { 285 mWifiManager.connect(mChannel, mKeyStoreNetworkId, mConnectListener); 286 } 287 mKeyStoreNetworkId = INVALID_NETWORK_ID; 288 289 updateAccessPoints(); 290 } 291 292 @Override 293 public void onPause() { 294 super.onPause(); 295 if (mWifiEnabler != null) { 296 mWifiEnabler.pause(); 297 } 298 getActivity().unregisterReceiver(mReceiver); 299 mScanner.pause(); 300 } 301 302 @Override 303 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 304 if (mShowMenu) { 305 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 306 menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc) 307 .setEnabled(wifiIsEnabled) 308 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 309 menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p) 310 .setEnabled(wifiIsEnabled) 311 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 312 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 313 .setEnabled(wifiIsEnabled) 314 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 315 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) 316 //.setIcon(R.drawable.ic_menu_scan_network) 317 .setEnabled(wifiIsEnabled) 318 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 319 menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin) 320 .setEnabled(wifiIsEnabled) 321 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 322 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 323 //.setIcon(android.R.drawable.ic_menu_manage) 324 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 325 } 326 super.onCreateOptionsMenu(menu, inflater); 327 } 328 329 @Override 330 public void onSaveInstanceState(Bundle outState) { 331 super.onSaveInstanceState(outState); 332 333 // If the dialog is showing, save its state. 334 if (mDialog != null && mDialog.isShowing()) { 335 outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit); 336 if (mDlgAccessPoint != null) { 337 mAccessPointSavedState = new Bundle(); 338 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 339 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 340 } 341 } 342 } 343 344 @Override 345 public boolean onOptionsItemSelected(MenuItem item) { 346 switch (item.getItemId()) { 347 case MENU_ID_WPS_PBC: 348 showDialog(WPS_PBC_DIALOG_ID); 349 return true; 350 case MENU_ID_P2P: 351 if (getActivity() instanceof PreferenceActivity) { 352 ((PreferenceActivity) getActivity()).startPreferencePanel( 353 WifiP2pSettings.class.getCanonicalName(), 354 null, 355 R.string.wifi_p2p_settings_title, null, 356 this, 0); 357 } else { 358 startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null); 359 } 360 return true; 361 case MENU_ID_WPS_PIN: 362 showDialog(WPS_PIN_DIALOG_ID); 363 return true; 364 case MENU_ID_SCAN: 365 if (mWifiManager.isWifiEnabled()) { 366 mScanner.forceScan(); 367 } 368 return true; 369 case MENU_ID_ADD_NETWORK: 370 if (mWifiManager.isWifiEnabled()) { 371 onAddNetworkPressed(); 372 } 373 return true; 374 case MENU_ID_ADVANCED: 375 if (getActivity() instanceof PreferenceActivity) { 376 ((PreferenceActivity) getActivity()).startPreferencePanel( 377 AdvancedWifiSettings.class.getCanonicalName(), 378 null, 379 R.string.wifi_advanced_titlebar, null, 380 this, 0); 381 } else { 382 startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null); 383 } 384 return true; 385 } 386 return super.onOptionsItemSelected(item); 387 } 388 389 @Override 390 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 391 if (info instanceof AdapterContextMenuInfo) { 392 Preference preference = (Preference) getListView().getItemAtPosition( 393 ((AdapterContextMenuInfo) info).position); 394 395 if (preference instanceof AccessPoint) { 396 mSelectedAccessPoint = (AccessPoint) preference; 397 menu.setHeaderTitle(mSelectedAccessPoint.ssid); 398 if (mSelectedAccessPoint.getLevel() != -1 399 && mSelectedAccessPoint.getState() == null) { 400 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 401 } 402 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 403 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 404 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 405 } 406 } 407 } 408 } 409 410 @Override 411 public boolean onContextItemSelected(MenuItem item) { 412 if (mSelectedAccessPoint == null) { 413 return super.onContextItemSelected(item); 414 } 415 switch (item.getItemId()) { 416 case MENU_ID_CONNECT: { 417 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 418 if (!requireKeyStore(mSelectedAccessPoint.getConfig())) { 419 mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId, 420 mConnectListener); 421 } 422 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { 423 /** Bypass dialog for unsecured networks */ 424 mSelectedAccessPoint.generateOpenNetworkConfig(); 425 mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(), 426 mConnectListener); 427 } else { 428 showDialog(mSelectedAccessPoint, true); 429 } 430 return true; 431 } 432 case MENU_ID_FORGET: { 433 mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener); 434 return true; 435 } 436 case MENU_ID_MODIFY: { 437 showDialog(mSelectedAccessPoint, true); 438 return true; 439 } 440 } 441 return super.onContextItemSelected(item); 442 } 443 444 @Override 445 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 446 if (preference instanceof AccessPoint) { 447 mSelectedAccessPoint = (AccessPoint) preference; 448 /** Bypass dialog for unsecured, unsaved networks */ 449 if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE && 450 mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 451 mSelectedAccessPoint.generateOpenNetworkConfig(); 452 mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(), mConnectListener); 453 } else { 454 showDialog(mSelectedAccessPoint, false); 455 } 456 } else { 457 return super.onPreferenceTreeClick(screen, preference); 458 } 459 return true; 460 } 461 462 private void showDialog(AccessPoint accessPoint, boolean edit) { 463 if (mDialog != null) { 464 removeDialog(WIFI_DIALOG_ID); 465 mDialog = null; 466 } 467 468 // Save the access point and edit mode 469 mDlgAccessPoint = accessPoint; 470 mDlgEdit = edit; 471 472 showDialog(WIFI_DIALOG_ID); 473 } 474 475 @Override 476 public Dialog onCreateDialog(int dialogId) { 477 switch (dialogId) { 478 case WIFI_DIALOG_ID: 479 AccessPoint ap = mDlgAccessPoint; // For manual launch 480 if (ap == null) { // For re-launch from saved state 481 if (mAccessPointSavedState != null) { 482 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 483 // For repeated orientation changes 484 mDlgAccessPoint = ap; 485 } 486 } 487 // If it's still null, fine, it's for Add Network 488 mSelectedAccessPoint = ap; 489 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit); 490 return mDialog; 491 case WPS_PBC_DIALOG_ID: 492 return new WpsDialog(getActivity(), WpsInfo.PBC); 493 case WPS_PIN_DIALOG_ID: 494 return new WpsDialog(getActivity(), WpsInfo.DISPLAY); 495 } 496 return super.onCreateDialog(dialogId); 497 } 498 499 private boolean requireKeyStore(WifiConfiguration config) { 500 if (WifiConfigController.requireKeyStore(config) && 501 KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) { 502 mKeyStoreNetworkId = config.networkId; 503 Credentials.getInstance().unlock(getActivity()); 504 return true; 505 } 506 return false; 507 } 508 509 /** 510 * Shows the latest access points available with supplimental information like 511 * the strength of network and the security for it. 512 */ 513 private void updateAccessPoints() { 514 // Safeguard from some delayed event handling 515 if (getActivity() == null) return; 516 517 final int wifiState = mWifiManager.getWifiState(); 518 519 switch (wifiState) { 520 case WifiManager.WIFI_STATE_ENABLED: 521 // AccessPoints are automatically sorted with TreeSet. 522 final Collection<AccessPoint> accessPoints = constructAccessPoints(); 523 getPreferenceScreen().removeAll(); 524 if(accessPoints.size() == 0) { 525 addMessagePreference(R.string.wifi_empty_list_wifi_on); 526 } 527 for (AccessPoint accessPoint : accessPoints) { 528 getPreferenceScreen().addPreference(accessPoint); 529 } 530 break; 531 532 case WifiManager.WIFI_STATE_ENABLING: 533 getPreferenceScreen().removeAll(); 534 break; 535 536 case WifiManager.WIFI_STATE_DISABLING: 537 addMessagePreference(R.string.wifi_stopping); 538 break; 539 540 case WifiManager.WIFI_STATE_DISABLED: 541 addMessagePreference(R.string.wifi_empty_list_wifi_off); 542 break; 543 } 544 } 545 546 private void addMessagePreference(int messageId) { 547 if (mEmptyView != null) mEmptyView.setText(messageId); 548 getPreferenceScreen().removeAll(); 549 } 550 551 /** Returns sorted list of access points */ 552 private List<AccessPoint> constructAccessPoints() { 553 ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 554 /** Lookup table to more quickly update AccessPoints by only considering objects with the 555 * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ 556 Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); 557 558 final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 559 if (configs != null) { 560 for (WifiConfiguration config : configs) { 561 AccessPoint accessPoint = new AccessPoint(getActivity(), config); 562 accessPoint.update(mLastInfo, mLastState); 563 accessPoints.add(accessPoint); 564 apMap.put(accessPoint.ssid, accessPoint); 565 } 566 } 567 568 final List<ScanResult> results = mWifiManager.getScanResults(); 569 if (results != null) { 570 for (ScanResult result : results) { 571 // Ignore hidden and ad-hoc networks. 572 if (result.SSID == null || result.SSID.length() == 0 || 573 result.capabilities.contains("[IBSS]")) { 574 continue; 575 } 576 577 boolean found = false; 578 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { 579 if (accessPoint.update(result)) 580 found = true; 581 } 582 if (!found) { 583 AccessPoint accessPoint = new AccessPoint(getActivity(), result); 584 accessPoints.add(accessPoint); 585 apMap.put(accessPoint.ssid, accessPoint); 586 } 587 } 588 } 589 590 // Pre-sort accessPoints to speed preference insertion 591 Collections.sort(accessPoints); 592 return accessPoints; 593 } 594 595 /** A restricted multimap for use in constructAccessPoints */ 596 private class Multimap<K,V> { 597 private HashMap<K,List<V>> store = new HashMap<K,List<V>>(); 598 /** retrieve a non-null list of values with key K */ 599 List<V> getAll(K key) { 600 List<V> values = store.get(key); 601 return values != null ? values : Collections.<V>emptyList(); 602 } 603 604 void put(K key, V val) { 605 List<V> curVals = store.get(key); 606 if (curVals == null) { 607 curVals = new ArrayList<V>(3); 608 store.put(key, curVals); 609 } 610 curVals.add(val); 611 } 612 } 613 614 private void handleEvent(Context context, Intent intent) { 615 String action = intent.getAction(); 616 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 617 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 618 WifiManager.WIFI_STATE_UNKNOWN)); 619 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || 620 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || 621 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { 622 updateAccessPoints(); 623 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 624 //Ignore supplicant state changes when network is connected 625 //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and 626 //introduce a broadcast that combines the supplicant and network 627 //network state change events so the apps dont have to worry about 628 //ignoring supplicant state change when network is connected 629 //to get more fine grained information. 630 SupplicantState state = (SupplicantState) intent.getParcelableExtra( 631 WifiManager.EXTRA_NEW_STATE); 632 if (!mConnected.get() && SupplicantState.isHandshakeState(state)) { 633 updateConnectionState(WifiInfo.getDetailedStateOf(state)); 634 } 635 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 636 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 637 WifiManager.EXTRA_NETWORK_INFO); 638 mConnected.set(info.isConnected()); 639 changeNextButtonState(info.isConnected()); 640 updateAccessPoints(); 641 updateConnectionState(info.getDetailedState()); 642 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 643 updateConnectionState(null); 644 } 645 } 646 647 private void updateConnectionState(DetailedState state) { 648 /* sticky broadcasts can call this when wifi is disabled */ 649 if (!mWifiManager.isWifiEnabled()) { 650 mScanner.pause(); 651 return; 652 } 653 654 if (state == DetailedState.OBTAINING_IPADDR) { 655 mScanner.pause(); 656 } else { 657 mScanner.resume(); 658 } 659 660 mLastInfo = mWifiManager.getConnectionInfo(); 661 if (state != null) { 662 mLastState = state; 663 } 664 665 for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) { 666 // Maybe there's a WifiConfigPreference 667 Preference preference = getPreferenceScreen().getPreference(i); 668 if (preference instanceof AccessPoint) { 669 final AccessPoint accessPoint = (AccessPoint) preference; 670 accessPoint.update(mLastInfo, mLastState); 671 } 672 } 673 } 674 675 private void updateWifiState(int state) { 676 getActivity().invalidateOptionsMenu(); 677 678 switch (state) { 679 case WifiManager.WIFI_STATE_ENABLED: 680 mScanner.resume(); 681 return; // not break, to avoid the call to pause() below 682 683 case WifiManager.WIFI_STATE_ENABLING: 684 addMessagePreference(R.string.wifi_starting); 685 break; 686 687 case WifiManager.WIFI_STATE_DISABLED: 688 addMessagePreference(R.string.wifi_empty_list_wifi_off); 689 break; 690 } 691 692 mLastInfo = null; 693 mLastState = null; 694 mScanner.pause(); 695 } 696 697 private class Scanner extends Handler { 698 private int mRetry = 0; 699 700 void resume() { 701 if (!hasMessages(0)) { 702 sendEmptyMessage(0); 703 } 704 } 705 706 void forceScan() { 707 removeMessages(0); 708 sendEmptyMessage(0); 709 } 710 711 void pause() { 712 mRetry = 0; 713 removeMessages(0); 714 } 715 716 @Override 717 public void handleMessage(Message message) { 718 if (mWifiManager.startScanActive()) { 719 mRetry = 0; 720 } else if (++mRetry >= 3) { 721 mRetry = 0; 722 Toast.makeText(getActivity(), R.string.wifi_fail_to_scan, 723 Toast.LENGTH_LONG).show(); 724 return; 725 } 726 sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); 727 } 728 } 729 730 /** 731 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 732 * Wifi setup screens, not in usual wifi settings screen. 733 * 734 * @param connected true when the device is connected to a wifi network. 735 */ 736 private void changeNextButtonState(boolean connected) { 737 if (mEnableNextOnConnection && hasNextButton()) { 738 getNextButton().setEnabled(connected); 739 } 740 } 741 742 public void onClick(DialogInterface dialogInterface, int button) { 743 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 744 forget(); 745 } else if (button == WifiDialog.BUTTON_SUBMIT) { 746 submit(mDialog.getController()); 747 } 748 } 749 750 /* package */ void submit(WifiConfigController configController) { 751 752 final WifiConfiguration config = configController.getConfig(); 753 754 if (config == null) { 755 if (mSelectedAccessPoint != null 756 && !requireKeyStore(mSelectedAccessPoint.getConfig()) 757 && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 758 mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId, 759 mConnectListener); 760 } 761 } else if (config.networkId != INVALID_NETWORK_ID) { 762 if (mSelectedAccessPoint != null) { 763 mWifiManager.save(mChannel, config, mSaveListener); 764 } 765 } else { 766 if (configController.isEdit() || requireKeyStore(config)) { 767 mWifiManager.save(mChannel, config, mSaveListener); 768 } else { 769 mWifiManager.connect(mChannel, config, mConnectListener); 770 } 771 } 772 773 if (mWifiManager.isWifiEnabled()) { 774 mScanner.resume(); 775 } 776 updateAccessPoints(); 777 } 778 779 /* package */ void forget() { 780 if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 781 // Should not happen, but a monkey seems to triger it 782 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 783 return; 784 } 785 786 mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener); 787 788 if (mWifiManager.isWifiEnabled()) { 789 mScanner.resume(); 790 } 791 updateAccessPoints(); 792 793 // We need to rename/replace "Next" button in wifi setup context. 794 changeNextButtonState(false); 795 } 796 797 /** 798 * Refreshes acccess points and ask Wifi module to scan networks again. 799 */ 800 /* package */ void refreshAccessPoints() { 801 if (mWifiManager.isWifiEnabled()) { 802 mScanner.resume(); 803 } 804 805 getPreferenceScreen().removeAll(); 806 } 807 808 /** 809 * Called when "add network" button is pressed. 810 */ 811 /* package */ void onAddNetworkPressed() { 812 // No exact access point is selected. 813 mSelectedAccessPoint = null; 814 showDialog(null, true); 815 } 816 817 /* package */ int getAccessPointsCount() { 818 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 819 if (wifiIsEnabled) { 820 return getPreferenceScreen().getPreferenceCount(); 821 } else { 822 return 0; 823 } 824 } 825 826 /** 827 * Requests wifi module to pause wifi scan. May be ignored when the module is disabled. 828 */ 829 /* package */ void pauseWifiScan() { 830 if (mWifiManager.isWifiEnabled()) { 831 mScanner.pause(); 832 } 833 } 834 835 /** 836 * Requests wifi module to resume wifi scan. May be ignored when the module is disabled. 837 */ 838 /* package */ void resumeWifiScan() { 839 if (mWifiManager.isWifiEnabled()) { 840 mScanner.resume(); 841 } 842 } 843 844 @Override 845 protected int getHelpResource() { 846 // Setup Wizard has different help content 847 if (mShowMenu) { 848 return R.string.help_url_wifi; 849 } 850 return 0; 851 } 852} 853