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