WifiSettings.java revision 06b65d1c7d5f5c87f8d723b6ab0040f9bfc834c4
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; 20import static android.os.UserManager.DISALLOW_CONFIG_WIFI; 21 22import android.app.Activity; 23import android.app.Dialog; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.res.Resources; 30import android.content.res.TypedArray; 31import android.location.LocationManager; 32import android.net.ConnectivityManager; 33import android.net.NetworkInfo; 34import android.net.NetworkInfo.DetailedState; 35import android.net.NetworkInfo.State; 36import android.net.wifi.ScanResult; 37import android.net.wifi.WifiConfiguration; 38import android.net.wifi.WifiInfo; 39import android.net.wifi.WifiManager; 40import android.net.wifi.WpsInfo; 41import android.os.Bundle; 42import android.os.Handler; 43import android.os.Message; 44import android.os.UserHandle; 45import android.preference.Preference; 46import android.preference.PreferenceScreen; 47import android.util.Log; 48import android.view.ContextMenu; 49import android.view.ContextMenu.ContextMenuInfo; 50import android.view.Menu; 51import android.view.MenuInflater; 52import android.view.MenuItem; 53import android.view.View; 54import android.widget.AdapterView.AdapterContextMenuInfo; 55import android.widget.TextView; 56import android.widget.Toast; 57 58import com.android.settings.R; 59import com.android.settings.RestrictedSettingsFragment; 60import com.android.settings.SettingsActivity; 61import com.android.settings.search.BaseSearchIndexProvider; 62import com.android.settings.search.Indexable; 63import com.android.settings.search.SearchIndexableRaw; 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 RestrictedSettingsFragment 81 implements DialogInterface.OnClickListener, Indexable { 82 83 private static final String TAG = "WifiSettings"; 84 85 /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST; 86 private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; 87 private static final int MENU_ID_SAVED_NETWORK = Menu.FIRST + 2; 88 /* package */ static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3; 89 private static final int MENU_ID_ADVANCED = Menu.FIRST + 4; 90 private static final int MENU_ID_SCAN = Menu.FIRST + 5; 91 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 92 private static final int MENU_ID_FORGET = Menu.FIRST + 7; 93 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 94 private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9; 95 private static final int MENU_ID_APPS = Menu.FIRST + 10; 96 97 public static final int WIFI_DIALOG_ID = 1; 98 /* package */ static final int WPS_PBC_DIALOG_ID = 2; 99 private static final int WPS_PIN_DIALOG_ID = 3; 100 private static final int WRITE_NFC_DIALOG_ID = 6; 101 102 // Combo scans can take 5-6s to complete - set to 10s. 103 private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; 104 105 // Instance state keys 106 private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode"; 107 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 108 109 private static boolean savedNetworksExist; 110 111 private final IntentFilter mFilter; 112 private final BroadcastReceiver mReceiver; 113 private final Scanner mScanner; 114 115 /* package */ WifiManager mWifiManager; 116 private WifiManager.ActionListener mConnectListener; 117 private WifiManager.ActionListener mSaveListener; 118 private WifiManager.ActionListener mForgetListener; 119 120 private WifiEnabler mWifiEnabler; 121 // An access point being editted is stored here. 122 private AccessPoint mSelectedAccessPoint; 123 124 private NetworkInfo mLastNetworkInfo; 125 private WifiInfo mLastInfo; 126 127 private final AtomicBoolean mConnected = new AtomicBoolean(false); 128 129 private WifiDialog mDialog; 130 private WriteWifiConfigToNfcDialog mWifiToNfcDialog; 131 132 private TextView mEmptyView; 133 134 private boolean showAppIcons = false; 135 private MenuItem showAppMenuItem = null; 136 137 // this boolean extra specifies whether to disable the Next button when not connected. Used by 138 // account creation outside of setup wizard. 139 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 140 // This string extra specifies a network to open the connect dialog on, so the user can enter 141 // network credentials. This is used by quick settings for secured networks. 142 private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 143 144 // should Next button only be enabled when we have a connection? 145 private boolean mEnableNextOnConnection; 146 147 // Save the dialog details 148 private boolean mDlgEdit; 149 private AccessPoint mDlgAccessPoint; 150 private Bundle mAccessPointSavedState; 151 152 /** verbose logging flag. this flag is set thru developer debugging options 153 * and used so as to assist with in-the-field WiFi connectivity debugging */ 154 public static int mVerboseLogging = 0; 155 156 /* End of "used in Wifi Setup context" */ 157 158 /** A restricted multimap for use in constructAccessPoints */ 159 private static class Multimap<K,V> { 160 private final HashMap<K,List<V>> store = new HashMap<K,List<V>>(); 161 /** retrieve a non-null list of values with key K */ 162 List<V> getAll(K key) { 163 List<V> values = store.get(key); 164 return values != null ? values : Collections.<V>emptyList(); 165 } 166 167 void put(K key, V val) { 168 List<V> curVals = store.get(key); 169 if (curVals == null) { 170 curVals = new ArrayList<V>(3); 171 store.put(key, curVals); 172 } 173 curVals.add(val); 174 } 175 } 176 177 private static class Scanner extends Handler { 178 private int mRetry = 0; 179 private WifiSettings mWifiSettings = null; 180 181 Scanner(WifiSettings wifiSettings) { 182 mWifiSettings = wifiSettings; 183 } 184 185 void resume() { 186 if (!hasMessages(0)) { 187 sendEmptyMessage(0); 188 } 189 } 190 191 void forceScan() { 192 removeMessages(0); 193 sendEmptyMessage(0); 194 } 195 196 void pause() { 197 mRetry = 0; 198 removeMessages(0); 199 } 200 201 @Override 202 public void handleMessage(Message message) { 203 if (mWifiSettings.mWifiManager.startScan()) { 204 mRetry = 0; 205 } else if (++mRetry >= 3) { 206 mRetry = 0; 207 Activity activity = mWifiSettings.getActivity(); 208 if (activity != null) { 209 Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show(); 210 } 211 return; 212 } 213 sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); 214 } 215 } 216 217 public WifiSettings() { 218 super(DISALLOW_CONFIG_WIFI); 219 mFilter = new IntentFilter(); 220 mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 221 mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 222 mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 223 mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 224 mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 225 mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 226 mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 227 mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 228 229 mReceiver = new BroadcastReceiver() { 230 @Override 231 public void onReceive(Context context, Intent intent) { 232 handleEvent(intent); 233 } 234 }; 235 236 mScanner = new Scanner(this); 237 } 238 239 @Override 240 public void onActivityCreated(Bundle savedInstanceState) { 241 super.onActivityCreated(savedInstanceState); 242 243 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 244 245 mConnectListener = new WifiManager.ActionListener() { 246 @Override 247 public void onSuccess() { 248 } 249 @Override 250 public void onFailure(int reason) { 251 Activity activity = getActivity(); 252 if (activity != null) { 253 Toast.makeText(activity, 254 R.string.wifi_failed_connect_message, 255 Toast.LENGTH_SHORT).show(); 256 } 257 } 258 }; 259 260 mSaveListener = new WifiManager.ActionListener() { 261 @Override 262 public void onSuccess() { 263 } 264 @Override 265 public void onFailure(int reason) { 266 Activity activity = getActivity(); 267 if (activity != null) { 268 Toast.makeText(activity, 269 R.string.wifi_failed_save_message, 270 Toast.LENGTH_SHORT).show(); 271 } 272 } 273 }; 274 275 mForgetListener = new WifiManager.ActionListener() { 276 @Override 277 public void onSuccess() { 278 } 279 @Override 280 public void onFailure(int reason) { 281 Activity activity = getActivity(); 282 if (activity != null) { 283 Toast.makeText(activity, 284 R.string.wifi_failed_forget_message, 285 Toast.LENGTH_SHORT).show(); 286 } 287 } 288 }; 289 290 if (savedInstanceState != null) { 291 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); 292 if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 293 mAccessPointSavedState = 294 savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 295 } 296 } 297 298 // if we're supposed to enable/disable the Next button based on our current connection 299 // state, start it off in the right state 300 Intent intent = getActivity().getIntent(); 301 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 302 303 if (mEnableNextOnConnection) { 304 if (hasNextButton()) { 305 final ConnectivityManager connectivity = (ConnectivityManager) 306 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 307 if (connectivity != null) { 308 NetworkInfo info = connectivity.getNetworkInfo( 309 ConnectivityManager.TYPE_WIFI); 310 changeNextButtonState(info.isConnected()); 311 } 312 } 313 } 314 315 addPreferencesFromResource(R.xml.wifi_settings); 316 317 mEmptyView = initEmptyView(); 318 registerForContextMenu(getListView()); 319 setHasOptionsMenu(true); 320 321 if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) { 322 String ssid = intent.getStringExtra(EXTRA_START_CONNECT_SSID); 323 updateAccessPoints(); 324 PreferenceScreen preferenceScreen = getPreferenceScreen(); 325 for (int i = 0; i < preferenceScreen.getPreferenceCount(); i++) { 326 Preference preference = preferenceScreen.getPreference(i); 327 if (preference instanceof AccessPoint) { 328 AccessPoint accessPoint = (AccessPoint) preference; 329 if (ssid.equals(accessPoint.ssid) && accessPoint.networkId == -1 330 && accessPoint.security != AccessPoint.SECURITY_NONE) { 331 onPreferenceTreeClick(preferenceScreen, preference); 332 break; 333 } 334 } 335 } 336 } 337 } 338 339 @Override 340 public void onDestroyView() { 341 super.onDestroyView(); 342 343 if (mWifiEnabler != null) { 344 mWifiEnabler.teardownSwitchBar(); 345 } 346 } 347 348 @Override 349 public void onStart() { 350 super.onStart(); 351 352 // On/off switch is hidden for Setup Wizard (returns null) 353 mWifiEnabler = createWifiEnabler(); 354 } 355 356 /** 357 * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard) 358 */ 359 /* package */ WifiEnabler createWifiEnabler() { 360 final SettingsActivity activity = (SettingsActivity) getActivity(); 361 return new WifiEnabler(activity, activity.getSwitchBar()); 362 } 363 364 @Override 365 public void onResume() { 366 final Activity activity = getActivity(); 367 super.onResume(); 368 if (mWifiEnabler != null) { 369 mWifiEnabler.resume(activity); 370 } 371 372 activity.registerReceiver(mReceiver, mFilter); 373 updateAccessPoints(); 374 } 375 376 @Override 377 public void onPause() { 378 super.onPause(); 379 if (mWifiEnabler != null) { 380 mWifiEnabler.pause(); 381 } 382 383 getActivity().unregisterReceiver(mReceiver); 384 mScanner.pause(); 385 } 386 387 @Override 388 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 389 // If the user is not allowed to configure wifi, do not show the menu. 390 if (isUiRestricted()) return; 391 392 addOptionsMenuItems(menu); 393 super.onCreateOptionsMenu(menu, inflater); 394 } 395 396 /** 397 * @param menu 398 */ 399 void addOptionsMenuItems(Menu menu) { 400 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 401 TypedArray ta = getActivity().getTheme().obtainStyledAttributes( 402 new int[] {R.attr.ic_menu_add, R.attr.ic_wps}); 403 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 404 .setIcon(ta.getDrawable(0)) 405 .setEnabled(wifiIsEnabled) 406 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 407 if (savedNetworksExist) { 408 menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label) 409 .setIcon(ta.getDrawable(0)) 410 .setEnabled(wifiIsEnabled) 411 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 412 } 413 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh) 414 .setEnabled(wifiIsEnabled) 415 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 416 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 417 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 418 showAppMenuItem = menu.add(Menu.NONE, MENU_ID_APPS, 0, R.string.wifi_menu_apps); 419 showAppMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 420 ta.recycle(); 421 } 422 423 @Override 424 public void onSaveInstanceState(Bundle outState) { 425 super.onSaveInstanceState(outState); 426 427 // If the dialog is showing, save its state. 428 if (mDialog != null && mDialog.isShowing()) { 429 outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit); 430 if (mDlgAccessPoint != null) { 431 mAccessPointSavedState = new Bundle(); 432 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 433 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 434 } 435 } 436 } 437 438 @Override 439 public boolean onOptionsItemSelected(MenuItem item) { 440 // If the user is not allowed to configure wifi, do not handle menu selections. 441 if (isUiRestricted()) return false; 442 443 switch (item.getItemId()) { 444 case MENU_ID_WPS_PBC: 445 showDialog(WPS_PBC_DIALOG_ID); 446 return true; 447 /* 448 case MENU_ID_P2P: 449 if (getActivity() instanceof SettingsActivity) { 450 ((SettingsActivity) getActivity()).startPreferencePanel( 451 WifiP2pSettings.class.getCanonicalName(), 452 null, 453 R.string.wifi_p2p_settings_title, null, 454 this, 0); 455 } else { 456 startFragment(this, WifiP2pSettings.class.getCanonicalName(), 457 R.string.wifi_p2p_settings_title, -1, null); 458 } 459 return true; 460 */ 461 case MENU_ID_WPS_PIN: 462 showDialog(WPS_PIN_DIALOG_ID); 463 return true; 464 case MENU_ID_SCAN: 465 if (mWifiManager.isWifiEnabled()) { 466 mScanner.forceScan(); 467 } 468 return true; 469 case MENU_ID_ADD_NETWORK: 470 if (mWifiManager.isWifiEnabled()) { 471 onAddNetworkPressed(); 472 } 473 return true; 474 case MENU_ID_SAVED_NETWORK: 475 if (getActivity() instanceof SettingsActivity) { 476 ((SettingsActivity) getActivity()).startPreferencePanel( 477 SavedAccessPointsWifiSettings.class.getCanonicalName(), null, 478 R.string.wifi_saved_access_points_titlebar, null, this, 0); 479 } else { 480 startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(), 481 R.string.wifi_saved_access_points_titlebar, 482 -1 /* Do not request a result */, null); 483 } 484 return true; 485 case MENU_ID_ADVANCED: 486 if (getActivity() instanceof SettingsActivity) { 487 ((SettingsActivity) getActivity()).startPreferencePanel( 488 AdvancedWifiSettings.class.getCanonicalName(), null, 489 R.string.wifi_advanced_titlebar, null, this, 0); 490 } else { 491 startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), 492 R.string.wifi_advanced_titlebar, -1 /* Do not request a results */, 493 null); 494 } 495 return true; 496 case MENU_ID_APPS: 497 showAppIcons = !showAppIcons; 498 499 if (showAppIcons) { 500 showAppMenuItem.setTitle(R.string.wifi_menu_apps_strength); 501 } else { 502 showAppMenuItem.setTitle(R.string.wifi_menu_apps); 503 } 504 updateAccessPoints(); 505 return true; 506 } 507 return super.onOptionsItemSelected(item); 508 } 509 510 @Override 511 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 512 if (info instanceof AdapterContextMenuInfo) { 513 Preference preference = (Preference) getListView().getItemAtPosition( 514 ((AdapterContextMenuInfo) info).position); 515 516 if (preference instanceof AccessPoint) { 517 mSelectedAccessPoint = (AccessPoint) preference; 518 menu.setHeaderTitle(mSelectedAccessPoint.ssid); 519 if (mSelectedAccessPoint.getLevel() != -1) { 520 if (mSelectedAccessPoint.getState() == null) { 521 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 522 } 523 } 524 525 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID || 526 (mSelectedAccessPoint.getNetworkInfo() != null && 527 mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED)) { 528 // Allow forgetting a network if either the network is saved or ephemerally 529 // connected. (In the latter case, "forget" blacklists the network so it won't 530 // be used again, ephemerally). 531 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 532 } 533 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 534 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 535 536 if (mSelectedAccessPoint.security != AccessPoint.SECURITY_NONE) { 537 // Only allow writing of NFC tags for password-protected networks. 538 menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc); 539 } 540 } 541 } 542 } 543 } 544 545 @Override 546 public boolean onContextItemSelected(MenuItem item) { 547 if (mSelectedAccessPoint == null) { 548 return super.onContextItemSelected(item); 549 } 550 switch (item.getItemId()) { 551 case MENU_ID_CONNECT: { 552 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 553 connect(mSelectedAccessPoint.networkId); 554 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { 555 /** Bypass dialog for unsecured networks */ 556 mSelectedAccessPoint.generateOpenNetworkConfig(); 557 connect(mSelectedAccessPoint.getConfig()); 558 } else { 559 showDialog(mSelectedAccessPoint, true); 560 } 561 return true; 562 } 563 case MENU_ID_FORGET: { 564 forget(); 565 return true; 566 } 567 case MENU_ID_MODIFY: { 568 showDialog(mSelectedAccessPoint, true); 569 return true; 570 } 571 case MENU_ID_WRITE_NFC: 572 showDialog(WRITE_NFC_DIALOG_ID); 573 return true; 574 575 } 576 return super.onContextItemSelected(item); 577 } 578 579 @Override 580 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 581 if (preference instanceof AccessPoint) { 582 mSelectedAccessPoint = (AccessPoint) preference; 583 /** Bypass dialog for unsecured, unsaved, and inactive networks */ 584 if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE && 585 mSelectedAccessPoint.networkId == INVALID_NETWORK_ID && 586 !mSelectedAccessPoint.isActive()) { 587 mSelectedAccessPoint.generateOpenNetworkConfig(); 588 if (!savedNetworksExist) { 589 savedNetworksExist = true; 590 getActivity().invalidateOptionsMenu(); 591 } 592 connect(mSelectedAccessPoint.getConfig()); 593 } else { 594 showDialog(mSelectedAccessPoint, false); 595 } 596 } else { 597 return super.onPreferenceTreeClick(screen, preference); 598 } 599 return true; 600 } 601 602 private void showDialog(AccessPoint accessPoint, boolean edit) { 603 if (mDialog != null) { 604 removeDialog(WIFI_DIALOG_ID); 605 mDialog = null; 606 } 607 608 // Save the access point and edit mode 609 mDlgAccessPoint = accessPoint; 610 mDlgEdit = edit; 611 612 showDialog(WIFI_DIALOG_ID); 613 } 614 615 @Override 616 public Dialog onCreateDialog(int dialogId) { 617 switch (dialogId) { 618 case WIFI_DIALOG_ID: 619 AccessPoint ap = mDlgAccessPoint; // For manual launch 620 if (ap == null) { // For re-launch from saved state 621 if (mAccessPointSavedState != null) { 622 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 623 // For repeated orientation changes 624 mDlgAccessPoint = ap; 625 // Reset the saved access point data 626 mAccessPointSavedState = null; 627 } 628 } 629 // If it's null, fine, it's for Add Network 630 mSelectedAccessPoint = ap; 631 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit); 632 return mDialog; 633 case WPS_PBC_DIALOG_ID: 634 return new WpsDialog(getActivity(), WpsInfo.PBC); 635 case WPS_PIN_DIALOG_ID: 636 return new WpsDialog(getActivity(), WpsInfo.DISPLAY); 637 case WRITE_NFC_DIALOG_ID: 638 if (mSelectedAccessPoint != null) { 639 mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( 640 getActivity(), mSelectedAccessPoint, mWifiManager); 641 return mWifiToNfcDialog; 642 } 643 644 } 645 return super.onCreateDialog(dialogId); 646 } 647 648 /** 649 * Shows the latest access points available with supplemental information like 650 * the strength of network and the security for it. 651 */ 652 private void updateAccessPoints() { 653 // Safeguard from some delayed event handling 654 if (getActivity() == null) return; 655 656 if (isUiRestricted()) { 657 addMessagePreference(R.string.wifi_empty_list_user_restricted); 658 return; 659 } 660 final int wifiState = mWifiManager.getWifiState(); 661 662 //when we update the screen, check if verbose logging has been turned on or off 663 mVerboseLogging = mWifiManager.getVerboseLoggingLevel(); 664 665 switch (wifiState) { 666 case WifiManager.WIFI_STATE_ENABLED: 667 // AccessPoints are automatically sorted with TreeSet. 668 final Collection<AccessPoint> accessPoints = 669 constructAccessPoints(getActivity(), mWifiManager, mLastInfo, 670 mLastNetworkInfo); 671 getPreferenceScreen().removeAll(); 672 if (accessPoints.size() == 0) { 673 addMessagePreference(R.string.wifi_empty_list_wifi_on); 674 } 675 676 for (AccessPoint accessPoint : accessPoints) { 677 if (showAppIcons) { 678 accessPoint.showAppIcon(); 679 } 680 681 // Ignore access points that are out of range. 682 if (accessPoint.getLevel() != -1) { 683 getPreferenceScreen().addPreference(accessPoint); 684 } 685 } 686 break; 687 688 case WifiManager.WIFI_STATE_ENABLING: 689 getPreferenceScreen().removeAll(); 690 break; 691 692 case WifiManager.WIFI_STATE_DISABLING: 693 addMessagePreference(R.string.wifi_stopping); 694 break; 695 696 case WifiManager.WIFI_STATE_DISABLED: 697 setOffMessage(); 698 break; 699 } 700 } 701 702 protected TextView initEmptyView() { 703 TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty); 704 getListView().setEmptyView(emptyView); 705 return emptyView; 706 } 707 708 private void setOffMessage() { 709 if (mEmptyView != null) { 710 mEmptyView.setText(R.string.wifi_empty_list_wifi_off); 711 if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(), 712 android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) { 713 mEmptyView.append("\n\n"); 714 int resId; 715 if (android.provider.Settings.Secure.isLocationProviderEnabled( 716 getActivity().getContentResolver(), LocationManager.NETWORK_PROVIDER)) { 717 resId = R.string.wifi_scan_notify_text_location_on; 718 } else { 719 resId = R.string.wifi_scan_notify_text_location_off; 720 } 721 CharSequence charSeq = getText(resId); 722 mEmptyView.append(charSeq); 723 } 724 } 725 getPreferenceScreen().removeAll(); 726 } 727 728 private void addMessagePreference(int messageId) { 729 if (mEmptyView != null) mEmptyView.setText(messageId); 730 getPreferenceScreen().removeAll(); 731 } 732 733 /** Returns sorted list of access points */ 734 private static List<AccessPoint> constructAccessPoints(Context context, 735 WifiManager wifiManager, WifiInfo lastInfo, NetworkInfo lastNetworkInfo) { 736 ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 737 /** Lookup table to more quickly update AccessPoints by only considering objects with the 738 * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ 739 Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); 740 741 final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks(); 742 if (configs != null) { 743 // Update "Saved Networks" menu option. 744 if (savedNetworksExist != (configs.size() > 0)) { 745 savedNetworksExist = !savedNetworksExist; 746 if (context instanceof Activity) { 747 ((Activity) context).invalidateOptionsMenu(); 748 } 749 } 750 for (WifiConfiguration config : configs) { 751 if (config.selfAdded && config.numAssociation == 0) { 752 continue; 753 } 754 AccessPoint accessPoint = new AccessPoint(context, config); 755 if (lastInfo != null && lastNetworkInfo != null) { 756 accessPoint.update(lastInfo, lastNetworkInfo); 757 } 758 accessPoints.add(accessPoint); 759 apMap.put(accessPoint.ssid, accessPoint); 760 } 761 } 762 763 final List<ScanResult> results = wifiManager.getScanResults(); 764 if (results != null) { 765 for (ScanResult result : results) { 766 // Ignore hidden and ad-hoc networks. 767 if (result.SSID == null || result.SSID.length() == 0 || 768 result.capabilities.contains("[IBSS]")) { 769 continue; 770 } 771 772 boolean found = false; 773 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { 774 if (accessPoint.update(result)) 775 found = true; 776 } 777 if (!found) { 778 AccessPoint accessPoint = new AccessPoint(context, result); 779 if (lastInfo != null && lastNetworkInfo != null) { 780 accessPoint.update(lastInfo, lastNetworkInfo); 781 } 782 accessPoints.add(accessPoint); 783 apMap.put(accessPoint.ssid, accessPoint); 784 } 785 } 786 } 787 788 // Pre-sort accessPoints to speed preference insertion 789 Collections.sort(accessPoints); 790 return accessPoints; 791 } 792 793 private void handleEvent(Intent intent) { 794 String action = intent.getAction(); 795 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 796 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 797 WifiManager.WIFI_STATE_UNKNOWN)); 798 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || 799 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || 800 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { 801 updateAccessPoints(); 802 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 803 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 804 WifiManager.EXTRA_NETWORK_INFO); 805 mConnected.set(info.isConnected()); 806 changeNextButtonState(info.isConnected()); 807 updateAccessPoints(); 808 updateNetworkInfo(info); 809 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 810 updateNetworkInfo(null); 811 } 812 } 813 814 private void updateNetworkInfo(NetworkInfo networkInfo) { 815 /* sticky broadcasts can call this when wifi is disabled */ 816 if (!mWifiManager.isWifiEnabled()) { 817 mScanner.pause(); 818 return; 819 } 820 821 if (networkInfo != null && 822 networkInfo.getDetailedState() == DetailedState.OBTAINING_IPADDR) { 823 mScanner.pause(); 824 } else { 825 mScanner.resume(); 826 } 827 828 mLastInfo = mWifiManager.getConnectionInfo(); 829 if (networkInfo != null) { 830 mLastNetworkInfo = networkInfo; 831 } 832 833 for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) { 834 // Maybe there's a WifiConfigPreference 835 Preference preference = getPreferenceScreen().getPreference(i); 836 if (preference instanceof AccessPoint) { 837 final AccessPoint accessPoint = (AccessPoint) preference; 838 accessPoint.update(mLastInfo, mLastNetworkInfo); 839 } 840 } 841 } 842 843 private void updateWifiState(int state) { 844 Activity activity = getActivity(); 845 if (activity != null) { 846 activity.invalidateOptionsMenu(); 847 } 848 849 switch (state) { 850 case WifiManager.WIFI_STATE_ENABLED: 851 mScanner.resume(); 852 return; // not break, to avoid the call to pause() below 853 854 case WifiManager.WIFI_STATE_ENABLING: 855 addMessagePreference(R.string.wifi_starting); 856 break; 857 858 case WifiManager.WIFI_STATE_DISABLED: 859 setOffMessage(); 860 break; 861 } 862 863 mLastInfo = null; 864 mLastNetworkInfo = null; 865 mScanner.pause(); 866 } 867 868 /** 869 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 870 * Wifi setup screens, not in usual wifi settings screen. 871 * 872 * @param enabled true when the device is connected to a wifi network. 873 */ 874 private void changeNextButtonState(boolean enabled) { 875 if (mEnableNextOnConnection && hasNextButton()) { 876 getNextButton().setEnabled(enabled); 877 } 878 } 879 880 @Override 881 public void onClick(DialogInterface dialogInterface, int button) { 882 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 883 forget(); 884 } else if (button == WifiDialog.BUTTON_SUBMIT) { 885 if (mDialog != null) { 886 submit(mDialog.getController()); 887 } 888 } 889 } 890 891 /* package */ void submit(WifiConfigController configController) { 892 893 final WifiConfiguration config = configController.getConfig(); 894 895 if (config == null) { 896 if (mSelectedAccessPoint != null 897 && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 898 connect(mSelectedAccessPoint.networkId); 899 } 900 } else if (config.networkId != INVALID_NETWORK_ID) { 901 if (mSelectedAccessPoint != null) { 902 mWifiManager.save(config, mSaveListener); 903 } 904 } else { 905 if (configController.isEdit()) { 906 mWifiManager.save(config, mSaveListener); 907 } else { 908 connect(config); 909 } 910 } 911 912 if (mWifiManager.isWifiEnabled()) { 913 mScanner.resume(); 914 } 915 updateAccessPoints(); 916 } 917 918 /* package */ void forget() { 919 if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 920 if (mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) { 921 // Network is active but has no network ID - must be ephemeral. 922 mWifiManager.disableEphemeralNetwork( 923 AccessPoint.convertToQuotedString(mSelectedAccessPoint.ssid)); 924 } else { 925 // Should not happen, but a monkey seems to trigger it 926 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 927 return; 928 } 929 } else { 930 mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener); 931 } 932 933 934 if (mWifiManager.isWifiEnabled()) { 935 mScanner.resume(); 936 } 937 updateAccessPoints(); 938 939 // We need to rename/replace "Next" button in wifi setup context. 940 changeNextButtonState(false); 941 } 942 943 protected void connect(final WifiConfiguration config) { 944 mWifiManager.connect(config, mConnectListener); 945 } 946 947 protected void connect(final int networkId) { 948 mWifiManager.connect(networkId, mConnectListener); 949 } 950 951 /** 952 * Refreshes acccess points and ask Wifi module to scan networks again. 953 */ 954 /* package */ void refreshAccessPoints() { 955 if (mWifiManager.isWifiEnabled()) { 956 mScanner.resume(); 957 } 958 959 getPreferenceScreen().removeAll(); 960 } 961 962 /** 963 * Called when "add network" button is pressed. 964 */ 965 /* package */ void onAddNetworkPressed() { 966 // No exact access point is selected. 967 mSelectedAccessPoint = null; 968 showDialog(null, true); 969 } 970 971 /* package */ int getAccessPointsCount() { 972 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 973 if (wifiIsEnabled) { 974 return getPreferenceScreen().getPreferenceCount(); 975 } else { 976 return 0; 977 } 978 } 979 980 /** 981 * Requests wifi module to pause wifi scan. May be ignored when the module is disabled. 982 */ 983 /* package */ void pauseWifiScan() { 984 if (mWifiManager.isWifiEnabled()) { 985 mScanner.pause(); 986 } 987 } 988 989 /** 990 * Requests wifi module to resume wifi scan. May be ignored when the module is disabled. 991 */ 992 /* package */ void resumeWifiScan() { 993 if (mWifiManager.isWifiEnabled()) { 994 mScanner.resume(); 995 } 996 } 997 998 @Override 999 protected int getHelpResource() { 1000 return R.string.help_url_wifi; 1001 } 1002 1003 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1004 new BaseSearchIndexProvider() { 1005 @Override 1006 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 1007 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); 1008 final Resources res = context.getResources(); 1009 1010 // Add fragment title 1011 SearchIndexableRaw data = new SearchIndexableRaw(context); 1012 data.title = res.getString(R.string.wifi_settings); 1013 data.screenTitle = res.getString(R.string.wifi_settings); 1014 data.keywords = res.getString(R.string.keywords_wifi); 1015 result.add(data); 1016 1017 // Add available Wi-Fi access points 1018 WifiManager wifiManager = 1019 (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 1020 final Collection<AccessPoint> accessPoints = 1021 constructAccessPoints(context, wifiManager, null, null); 1022 for (AccessPoint accessPoint : accessPoints) { 1023 // We are indexing only the saved Wi-Fi networks. 1024 if (accessPoint.getConfig() == null) continue; 1025 data = new SearchIndexableRaw(context); 1026 data.title = accessPoint.getTitle().toString(); 1027 data.screenTitle = res.getString(R.string.wifi_settings); 1028 data.enabled = enabled; 1029 result.add(data); 1030 } 1031 1032 return result; 1033 } 1034 }; 1035} 1036