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