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