WifiSettings.java revision b759516cbf26c9bb5f71c7f93e764fac73f48701
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 android.annotation.NonNull; 20import android.app.Activity; 21import android.app.Dialog; 22import android.app.admin.DevicePolicyManager; 23import android.content.ComponentName; 24import android.content.ContentResolver; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.PackageManager; 28import android.content.pm.PackageManager.NameNotFoundException; 29import android.content.res.Resources; 30import android.net.ConnectivityManager; 31import android.net.NetworkInfo; 32import android.net.NetworkInfo.State; 33import android.net.wifi.WifiConfiguration; 34import android.net.wifi.WifiManager; 35import android.net.wifi.WpsInfo; 36import android.nfc.NfcAdapter; 37import android.os.Bundle; 38import android.os.HandlerThread; 39import android.os.Process; 40import android.provider.Settings; 41import android.support.annotation.VisibleForTesting; 42import android.support.v7.preference.Preference; 43import android.support.v7.preference.PreferenceCategory; 44import android.support.v7.preference.PreferenceManager; 45import android.support.v7.preference.PreferenceViewHolder; 46import android.text.TextUtils; 47import android.util.Log; 48import android.view.ContextMenu; 49import android.view.ContextMenu.ContextMenuInfo; 50import android.view.Menu; 51import android.view.MenuInflater; 52import android.view.MenuItem; 53import android.view.View; 54import android.widget.ProgressBar; 55import android.widget.Toast; 56 57import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 58import com.android.settings.LinkifyUtils; 59import com.android.settings.R; 60import com.android.settings.RestrictedSettingsFragment; 61import com.android.settings.SettingsActivity; 62import com.android.settings.dashboard.SummaryLoader; 63import com.android.settings.location.ScanningSettings; 64import com.android.settings.search.BaseSearchIndexProvider; 65import com.android.settings.search.Indexable; 66import com.android.settings.search.SearchIndexableRaw; 67import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; 68import com.android.settings.widget.SwitchBarController; 69import com.android.settingslib.RestrictedLockUtils; 70import com.android.settingslib.wifi.AccessPoint; 71import com.android.settingslib.wifi.AccessPoint.AccessPointListener; 72import com.android.settingslib.wifi.AccessPointPreference; 73import com.android.settingslib.wifi.WifiTracker; 74 75import java.util.ArrayList; 76import java.util.List; 77 78import static android.os.UserManager.DISALLOW_CONFIG_WIFI; 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 Indexable, WifiTracker.WifiListener, AccessPointListener, 90 WifiDialog.WifiDialogListener { 91 92 private static final String TAG = "WifiSettings"; 93 94 /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST; 95 private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; 96 private static final int MENU_ID_SCAN = Menu.FIRST + 5; 97 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 98 private static final int MENU_ID_FORGET = Menu.FIRST + 7; 99 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 100 private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9; 101 102 public static final int WIFI_DIALOG_ID = 1; 103 /* package */ static final int WPS_PBC_DIALOG_ID = 2; 104 private static final int WPS_PIN_DIALOG_ID = 3; 105 private static final int WRITE_NFC_DIALOG_ID = 6; 106 107 // Instance state keys 108 private static final String SAVE_DIALOG_MODE = "dialog_mode"; 109 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 110 private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state"; 111 112 private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; 113 private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; 114 private static final String PREF_KEY_ACCESS_POINTS = "access_points"; 115 private static final String PREF_KEY_ADDITIONAL_SETTINGS = "additional_settings"; 116 private static final String PREF_KEY_CONFIGURE_WIFI_SETTINGS = "configure_settings"; 117 private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks"; 118 119 protected WifiManager mWifiManager; 120 private WifiManager.ActionListener mConnectListener; 121 private WifiManager.ActionListener mSaveListener; 122 private WifiManager.ActionListener mForgetListener; 123 124 private WifiEnabler mWifiEnabler; 125 // An access point being editted is stored here. 126 private AccessPoint mSelectedAccessPoint; 127 128 private WifiDialog mDialog; 129 private WriteWifiConfigToNfcDialog mWifiToNfcDialog; 130 131 private ProgressBar mProgressHeader; 132 133 // this boolean extra specifies whether to disable the Next button when not connected. Used by 134 // account creation outside of setup wizard. 135 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 136 // This string extra specifies a network to open the connect dialog on, so the user can enter 137 // network credentials. This is used by quick settings for secured networks. 138 private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 139 140 // should Next button only be enabled when we have a connection? 141 private boolean mEnableNextOnConnection; 142 143 // should see all networks instead of collapsing networks and showing mSeeAllNetworksPreference. 144 private boolean mSeeAllNetworks; 145 private static final int NETWORKS_TO_INITIALLY_SHOW = 5; 146 147 // Save the dialog details 148 private int mDialogMode; 149 private AccessPoint mDlgAccessPoint; 150 private Bundle mAccessPointSavedState; 151 private Bundle mWifiNfcDialogSavedState; 152 153 private WifiTracker mWifiTracker; 154 private String mOpenSsid; 155 156 private HandlerThread mBgThread; 157 158 private AccessPointPreference.UserBadgeCache mUserBadgeCache; 159 160 private PreferenceCategory mConnectedAccessPointPreferenceCategory; 161 private PreferenceCategory mAccessPointsPreferenceCategory; 162 private PreferenceCategory mAdditionalSettingsPreferenceCategory; 163 private Preference mAddPreference; 164 private Preference mSeeAllNetworksPreference; 165 private Preference mConfigureWifiSettingsPreference; 166 private Preference mSavedNetworksPreference; 167 private LinkablePreference mStatusMessagePreference; 168 169 private MenuItem mScanMenuItem; 170 171 // For Search 172 private static final String DATA_KEY_REFERENCE = "main_toggle_wifi"; 173 174 /* End of "used in Wifi Setup context" */ 175 176 public WifiSettings() { 177 super(DISALLOW_CONFIG_WIFI); 178 } 179 180 @Override 181 public void onViewCreated(View view, Bundle savedInstanceState) { 182 super.onViewCreated(view, savedInstanceState); 183 final Activity activity = getActivity(); 184 if (activity != null) { 185 if (!isUiRestricted()) { 186 mProgressHeader = (ProgressBar) setPinnedHeaderView(R.layout.wifi_progress_header); 187 } 188 } 189 } 190 191 @Override 192 public void onCreate(Bundle icicle) { 193 super.onCreate(icicle); 194 getPreferenceManager().setPreferenceComparisonCallback( 195 new PreferenceManager.SimplePreferenceComparisonCallback()); 196 addPreferencesFromResource(R.xml.wifi_settings); 197 198 mConnectedAccessPointPreferenceCategory = 199 (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); 200 201 mAccessPointsPreferenceCategory = 202 (PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS); 203 mAdditionalSettingsPreferenceCategory = 204 (PreferenceCategory) findPreference(PREF_KEY_ADDITIONAL_SETTINGS); 205 mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS); 206 mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS); 207 208 if (isUiRestricted()) { 209 getPreferenceScreen().removePreference(mAdditionalSettingsPreferenceCategory); 210 } 211 212 Context prefContext = getPrefContext(); 213 mAddPreference = new Preference(prefContext); 214 mAddPreference.setIcon(R.drawable.ic_menu_add_inset); 215 mAddPreference.setTitle(R.string.wifi_add_network); 216 mSeeAllNetworksPreference = new Preference(prefContext); 217 mSeeAllNetworksPreference.setIcon(R.drawable.ic_arrow_down_24dp); 218 mSeeAllNetworksPreference.setTitle(R.string.wifi_see_all_networks_button_title); 219 mSeeAllNetworks = false; 220 mStatusMessagePreference = new LinkablePreference(prefContext); 221 222 mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager()); 223 224 mBgThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); 225 mBgThread.start(); 226 } 227 228 @Override 229 public void onDestroy() { 230 mBgThread.quit(); 231 super.onDestroy(); 232 } 233 234 @Override 235 public void onActivityCreated(Bundle savedInstanceState) { 236 super.onActivityCreated(savedInstanceState); 237 238 mWifiTracker = 239 new WifiTracker(getActivity(), this, mBgThread.getLooper(), true, true, false); 240 mWifiManager = mWifiTracker.getManager(); 241 242 mConnectListener = new WifiManager.ActionListener() { 243 @Override 244 public void onSuccess() { 245 } 246 @Override 247 public void onFailure(int reason) { 248 Activity activity = getActivity(); 249 if (activity != null) { 250 Toast.makeText(activity, 251 R.string.wifi_failed_connect_message, 252 Toast.LENGTH_SHORT).show(); 253 } 254 } 255 }; 256 257 mSaveListener = new WifiManager.ActionListener() { 258 @Override 259 public void onSuccess() { 260 } 261 @Override 262 public void onFailure(int reason) { 263 Activity activity = getActivity(); 264 if (activity != null) { 265 Toast.makeText(activity, 266 R.string.wifi_failed_save_message, 267 Toast.LENGTH_SHORT).show(); 268 } 269 } 270 }; 271 272 mForgetListener = new WifiManager.ActionListener() { 273 @Override 274 public void onSuccess() { 275 } 276 @Override 277 public void onFailure(int reason) { 278 Activity activity = getActivity(); 279 if (activity != null) { 280 Toast.makeText(activity, 281 R.string.wifi_failed_forget_message, 282 Toast.LENGTH_SHORT).show(); 283 } 284 } 285 }; 286 287 if (savedInstanceState != null) { 288 mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE); 289 if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 290 mAccessPointSavedState = 291 savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 292 } 293 294 if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) { 295 mWifiNfcDialogSavedState = 296 savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE); 297 } 298 } 299 300 // if we're supposed to enable/disable the Next button based on our current connection 301 // state, start it off in the right state 302 Intent intent = getActivity().getIntent(); 303 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 304 305 if (mEnableNextOnConnection) { 306 if (hasNextButton()) { 307 final ConnectivityManager connectivity = (ConnectivityManager) 308 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 309 if (connectivity != null) { 310 NetworkInfo info = connectivity.getNetworkInfo( 311 ConnectivityManager.TYPE_WIFI); 312 changeNextButtonState(info.isConnected()); 313 } 314 } 315 } 316 317 registerForContextMenu(getListView()); 318 setHasOptionsMenu(true); 319 320 if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) { 321 mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID); 322 onAccessPointsChanged(); 323 } 324 } 325 326 @Override 327 public void onDestroyView() { 328 super.onDestroyView(); 329 330 if (mWifiEnabler != null) { 331 mWifiEnabler.teardownSwitchController(); 332 } 333 } 334 335 @Override 336 public void onStart() { 337 super.onStart(); 338 339 // On/off switch is hidden for Setup Wizard (returns null) 340 mWifiEnabler = createWifiEnabler(); 341 } 342 343 /** 344 * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard) 345 */ 346 private WifiEnabler createWifiEnabler() { 347 final SettingsActivity activity = (SettingsActivity) getActivity(); 348 return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()), 349 mMetricsFeatureProvider); 350 } 351 352 @Override 353 public void onResume() { 354 final Activity activity = getActivity(); 355 super.onResume(); 356 if (mWifiEnabler != null) { 357 mWifiEnabler.resume(activity); 358 } 359 360 mWifiTracker.startTracking(); 361 activity.invalidateOptionsMenu(); 362 } 363 364 @Override 365 public void onPause() { 366 super.onPause(); 367 if (mWifiEnabler != null) { 368 mWifiEnabler.pause(); 369 } 370 371 mWifiTracker.stopTracking(); 372 } 373 374 @Override 375 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 376 // If the user is not allowed to configure wifi, do not show the menu. 377 if (isUiRestricted()) return; 378 379 addOptionsMenuItems(menu); 380 super.onCreateOptionsMenu(menu, inflater); 381 } 382 383 /** 384 * @param menu 385 */ 386 void addOptionsMenuItems(Menu menu) { 387 final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled(); 388 mScanMenuItem = menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh) 389 .setIcon(com.android.internal.R.drawable.ic_menu_refresh); 390 mScanMenuItem.setEnabled(wifiIsEnabled) 391 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 392 } 393 394 @Override 395 public int getMetricsCategory() { 396 return MetricsEvent.WIFI; 397 } 398 399 @Override 400 public void onSaveInstanceState(Bundle outState) { 401 super.onSaveInstanceState(outState); 402 403 // If the dialog is showing, save its state. 404 if (mDialog != null && mDialog.isShowing()) { 405 outState.putInt(SAVE_DIALOG_MODE, mDialogMode); 406 if (mDlgAccessPoint != null) { 407 mAccessPointSavedState = new Bundle(); 408 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 409 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 410 } 411 } 412 413 if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) { 414 Bundle savedState = new Bundle(); 415 mWifiToNfcDialog.saveState(savedState); 416 outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState); 417 } 418 } 419 420 @Override 421 public boolean onOptionsItemSelected(MenuItem item) { 422 // If the user is not allowed to configure wifi, do not handle menu selections. 423 if (isUiRestricted()) return false; 424 425 switch (item.getItemId()) { 426 case MENU_ID_WPS_PBC: 427 showDialog(WPS_PBC_DIALOG_ID); 428 return true; 429 /* 430 case MENU_ID_P2P: 431 if (getActivity() instanceof SettingsActivity) { 432 ((SettingsActivity) getActivity()).startPreferencePanel( 433 WifiP2pSettings.class.getCanonicalName(), 434 null, 435 R.string.wifi_p2p_settings_title, null, 436 this, 0); 437 } else { 438 startFragment(this, WifiP2pSettings.class.getCanonicalName(), 439 R.string.wifi_p2p_settings_title, -1, null); 440 } 441 return true; 442 */ 443 case MENU_ID_WPS_PIN: 444 showDialog(WPS_PIN_DIALOG_ID); 445 return true; 446 case MENU_ID_SCAN: 447 mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_FORCE_SCAN); 448 mWifiTracker.forceScan(); 449 return true; 450 } 451 return super.onOptionsItemSelected(item); 452 } 453 454 @Override 455 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 456 Preference preference = (Preference) view.getTag(); 457 458 if (preference instanceof LongPressAccessPointPreference) { 459 mSelectedAccessPoint = 460 ((LongPressAccessPointPreference) preference).getAccessPoint(); 461 menu.setHeaderTitle(mSelectedAccessPoint.getSsid()); 462 if (mSelectedAccessPoint.isConnectable()) { 463 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 464 } 465 466 WifiConfiguration config = mSelectedAccessPoint.getConfig(); 467 // Some configs are ineditable 468 if (isEditabilityLockedDown(getActivity(), config)) { 469 return; 470 } 471 472 if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) { 473 // Allow forgetting a network if either the network is saved or ephemerally 474 // connected. (In the latter case, "forget" blacklists the network so it won't 475 // be used again, ephemerally). 476 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 477 } 478 if (mSelectedAccessPoint.isSaved()) { 479 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 480 NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); 481 if (nfcAdapter != null && nfcAdapter.isEnabled() && 482 mSelectedAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { 483 // Only allow writing of NFC tags for password-protected networks. 484 menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc); 485 } 486 } 487 } 488 } 489 490 @Override 491 public boolean onContextItemSelected(MenuItem item) { 492 if (mSelectedAccessPoint == null) { 493 return super.onContextItemSelected(item); 494 } 495 switch (item.getItemId()) { 496 case MENU_ID_CONNECT: { 497 boolean isSavedNetwork = mSelectedAccessPoint.isSaved(); 498 if (isSavedNetwork) { 499 connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); 500 } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) { 501 /** Bypass dialog for unsecured networks */ 502 mSelectedAccessPoint.generateOpenNetworkConfig(); 503 connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); 504 } else { 505 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 506 } 507 return true; 508 } 509 case MENU_ID_FORGET: { 510 forget(); 511 return true; 512 } 513 case MENU_ID_MODIFY: { 514 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY); 515 return true; 516 } 517 case MENU_ID_WRITE_NFC: 518 showDialog(WRITE_NFC_DIALOG_ID); 519 return true; 520 521 } 522 return super.onContextItemSelected(item); 523 } 524 525 @Override 526 public boolean onPreferenceTreeClick(Preference preference) { 527 if (preference instanceof LongPressAccessPointPreference) { 528 mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint(); 529 if (mSelectedAccessPoint == null) { 530 return false; 531 } 532 /** Bypass dialog for unsecured, unsaved, and inactive networks */ 533 if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE && 534 !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) { 535 mSelectedAccessPoint.generateOpenNetworkConfig(); 536 connect(mSelectedAccessPoint.getConfig(), false /* isSavedNetwork */); 537 } else if (mSelectedAccessPoint.isSaved()) { 538 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_VIEW); 539 } else { 540 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 541 } 542 } else if (preference == mAddPreference) { 543 onAddNetworkPressed(); 544 } else if (preference == mSeeAllNetworksPreference) { 545 mSeeAllNetworks = true; 546 onAccessPointsChanged(); 547 } else { 548 return super.onPreferenceTreeClick(preference); 549 } 550 return true; 551 } 552 553 private void showDialog(AccessPoint accessPoint, int dialogMode) { 554 if (accessPoint != null) { 555 WifiConfiguration config = accessPoint.getConfig(); 556 if (isEditabilityLockedDown(getActivity(), config) && accessPoint.isActive()) { 557 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), 558 RestrictedLockUtils.getDeviceOwner(getActivity())); 559 return; 560 } 561 } 562 563 if (mDialog != null) { 564 removeDialog(WIFI_DIALOG_ID); 565 mDialog = null; 566 } 567 568 // Save the access point and edit mode 569 mDlgAccessPoint = accessPoint; 570 mDialogMode = dialogMode; 571 572 showDialog(WIFI_DIALOG_ID); 573 } 574 575 @Override 576 public Dialog onCreateDialog(int dialogId) { 577 switch (dialogId) { 578 case WIFI_DIALOG_ID: 579 AccessPoint ap = mDlgAccessPoint; // For manual launch 580 if (ap == null) { // For re-launch from saved state 581 if (mAccessPointSavedState != null) { 582 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 583 // For repeated orientation changes 584 mDlgAccessPoint = ap; 585 // Reset the saved access point data 586 mAccessPointSavedState = null; 587 } 588 } 589 // If it's null, fine, it's for Add Network 590 mSelectedAccessPoint = ap; 591 mDialog = new WifiDialog(getActivity(), this, ap, mDialogMode, 592 /* no hide submit/connect */ false); 593 return mDialog; 594 case WPS_PBC_DIALOG_ID: 595 return new WpsDialog(getActivity(), WpsInfo.PBC); 596 case WPS_PIN_DIALOG_ID: 597 return new WpsDialog(getActivity(), WpsInfo.DISPLAY); 598 case WRITE_NFC_DIALOG_ID: 599 if (mSelectedAccessPoint != null) { 600 mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( 601 getActivity(), mSelectedAccessPoint.getConfig().networkId, 602 mSelectedAccessPoint.getSecurity(), 603 mWifiManager); 604 } else if (mWifiNfcDialogSavedState != null) { 605 mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( 606 getActivity(), mWifiNfcDialogSavedState, mWifiManager); 607 } 608 609 return mWifiToNfcDialog; 610 } 611 return super.onCreateDialog(dialogId); 612 } 613 614 @Override 615 public int getDialogMetricsCategory(int dialogId) { 616 switch (dialogId) { 617 case WIFI_DIALOG_ID: 618 return MetricsEvent.DIALOG_WIFI_AP_EDIT; 619 case WPS_PBC_DIALOG_ID: 620 return MetricsEvent.DIALOG_WIFI_PBC; 621 case WPS_PIN_DIALOG_ID: 622 return MetricsEvent.DIALOG_WIFI_PIN; 623 case WRITE_NFC_DIALOG_ID: 624 return MetricsEvent.DIALOG_WIFI_WRITE_NFC; 625 default: 626 return 0; 627 } 628 } 629 630 /** 631 * Shows the latest access points available with supplemental information like 632 * the strength of network and the security for it. 633 */ 634 @Override 635 public void onAccessPointsChanged() { 636 // Safeguard from some delayed event handling 637 if (getActivity() == null) return; 638 if (isUiRestricted()) { 639 removeConnectedAccessPointPreference(); 640 mAccessPointsPreferenceCategory.removeAll(); 641 if (!isUiRestrictedByOnlyAdmin()) { 642 addMessagePreference(R.string.wifi_empty_list_user_restricted); 643 } 644 return; 645 } 646 final int wifiState = mWifiManager.getWifiState(); 647 648 switch (wifiState) { 649 case WifiManager.WIFI_STATE_ENABLED: 650 // AccessPoints are sorted by the WifiTracker 651 final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 652 653 boolean hasAvailableAccessPoints = false; 654 mAccessPointsPreferenceCategory.removePreference(mStatusMessagePreference); 655 cacheRemoveAllPrefs(mAccessPointsPreferenceCategory); 656 657 int index = configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0; 658 boolean fewerNetworksThanLimit = 659 accessPoints.size() <= index + NETWORKS_TO_INITIALLY_SHOW; 660 int numAccessPointsToShow = mSeeAllNetworks || fewerNetworksThanLimit 661 ? accessPoints.size() : index + NETWORKS_TO_INITIALLY_SHOW; 662 663 for (; index < numAccessPointsToShow; index++) { 664 AccessPoint accessPoint = accessPoints.get(index); 665 // Ignore access points that are out of range. 666 if (accessPoint.getLevel() != -1) { 667 String key = accessPoint.getBssid(); 668 if (TextUtils.isEmpty(key)) { 669 key = accessPoint.getSsidStr(); 670 } 671 hasAvailableAccessPoints = true; 672 LongPressAccessPointPreference pref = (LongPressAccessPointPreference) 673 getCachedPreference(key); 674 if (pref != null) { 675 pref.setOrder(index); 676 continue; 677 } 678 LongPressAccessPointPreference 679 preference = createLongPressActionPointPreference(accessPoint); 680 preference.setKey(key); 681 preference.setOrder(index); 682 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr()) 683 && !accessPoint.isSaved() 684 && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { 685 onPreferenceTreeClick(preference); 686 mOpenSsid = null; 687 } 688 mAccessPointsPreferenceCategory.addPreference(preference); 689 accessPoint.setListener(this); 690 preference.refresh(); 691 } 692 } 693 removeCachedPrefs(mAccessPointsPreferenceCategory); 694 if (!hasAvailableAccessPoints) { 695 setProgressBarVisible(true); 696 Preference pref = new Preference(getContext()) { 697 @Override 698 public void onBindViewHolder(PreferenceViewHolder holder) { 699 super.onBindViewHolder(holder); 700 // Show a line on each side of add network. 701 holder.setDividerAllowedBelow(true); 702 } 703 }; 704 pref.setSelectable(false); 705 pref.setSummary(R.string.wifi_empty_list_wifi_on); 706 pref.setOrder(index++); 707 pref.setKey(PREF_KEY_EMPTY_WIFI_LIST); 708 mAccessPointsPreferenceCategory.addPreference(pref); 709 } else { 710 setProgressBarVisible(false); 711 } 712 if (mSeeAllNetworks || fewerNetworksThanLimit) { 713 mAccessPointsPreferenceCategory.removePreference(mSeeAllNetworksPreference); 714 mAddPreference.setOrder(index); 715 mAccessPointsPreferenceCategory.addPreference(mAddPreference); 716 } else { 717 mAccessPointsPreferenceCategory.removePreference(mAddPreference); 718 mSeeAllNetworksPreference.setOrder(index); 719 mAccessPointsPreferenceCategory.addPreference(mSeeAllNetworksPreference); 720 } 721 setConfigureWifiSettingsVisibility(); 722 if (mScanMenuItem != null) { 723 mScanMenuItem.setEnabled(true); 724 } 725 break; 726 727 case WifiManager.WIFI_STATE_ENABLING: 728 removeConnectedAccessPointPreference(); 729 mAccessPointsPreferenceCategory.removeAll(); 730 setProgressBarVisible(true); 731 break; 732 733 case WifiManager.WIFI_STATE_DISABLING: 734 addMessagePreference(R.string.wifi_stopping); 735 setProgressBarVisible(true); 736 break; 737 738 case WifiManager.WIFI_STATE_DISABLED: 739 setOffMessage(); 740 setConfigureWifiSettingsVisibility(); 741 setProgressBarVisible(false); 742 if (mScanMenuItem != null) { 743 mScanMenuItem.setEnabled(false); 744 } 745 break; 746 } 747 } 748 749 @NonNull 750 private LongPressAccessPointPreference createLongPressActionPointPreference( 751 AccessPoint accessPoint) { 752 return new LongPressAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache, 753 false, R.drawable.ic_wifi_signal_0, this); 754 } 755 756 /** 757 * Configure the ConnectedAccessPointPreferenceCategory and return true if the Category was 758 * shown. 759 */ 760 private boolean configureConnectedAccessPointPreferenceCategory( 761 List<AccessPoint> accessPoints) { 762 if (accessPoints.size() == 0) { 763 removeConnectedAccessPointPreference(); 764 return false; 765 } 766 767 AccessPoint connectedAp = accessPoints.get(0); 768 if (!connectedAp.isActive()) { 769 removeConnectedAccessPointPreference(); 770 return false; 771 } 772 773 // Is the preference category empty? 774 if (mConnectedAccessPointPreferenceCategory.getPreferenceCount() == 0) { 775 addConnectedAccessPointPreference(connectedAp); 776 return true; 777 } 778 779 // Is the previous currently connected SSID different from the new one? 780 if (!((AccessPointPreference) 781 mConnectedAccessPointPreferenceCategory.getPreference(0)) 782 .getAccessPoint().getSsidStr().equals( 783 connectedAp.getSsidStr())) { 784 removeConnectedAccessPointPreference(); 785 addConnectedAccessPointPreference(connectedAp); 786 return true; 787 } 788 789 // Else same AP is connected, nothing to do 790 return true; 791 } 792 793 /** 794 * Creates a Preference for the given {@link AccessPoint} and adds it to the 795 * {@link #mConnectedAccessPointPreferenceCategory}. 796 */ 797 private void addConnectedAccessPointPreference(AccessPoint connectedAp) { 798 String key = connectedAp.getBssid(); 799 LongPressAccessPointPreference pref = (LongPressAccessPointPreference) 800 getCachedPreference(key); 801 if (pref == null) { 802 pref = createLongPressActionPointPreference(connectedAp); 803 } 804 pref.refresh(); 805 mConnectedAccessPointPreferenceCategory.addPreference(pref); 806 mConnectedAccessPointPreferenceCategory.setVisible(true); 807 } 808 809 /** Removes all preferences and hide the {@link #mConnectedAccessPointPreferenceCategory}. */ 810 private void removeConnectedAccessPointPreference() { 811 mConnectedAccessPointPreferenceCategory.removeAll(); 812 mConnectedAccessPointPreferenceCategory.setVisible(false); 813 } 814 815 private void setConfigureWifiSettingsVisibility() { 816 if (isUiRestricted()) { 817 mAdditionalSettingsPreferenceCategory.removeAll(); 818 return; 819 } 820 mAdditionalSettingsPreferenceCategory.addPreference(mConfigureWifiSettingsPreference); 821 if (mWifiTracker.doSavedNetworksExist()) { 822 mAdditionalSettingsPreferenceCategory.addPreference(mSavedNetworksPreference); 823 } else { 824 mAdditionalSettingsPreferenceCategory.removePreference(mSavedNetworksPreference); 825 } 826 } 827 828 private void setOffMessage() { 829 if (isUiRestricted()) { 830 removeConnectedAccessPointPreference(); 831 mAccessPointsPreferenceCategory.removeAll(); 832 if (!isUiRestrictedByOnlyAdmin()) { 833 addMessagePreference(R.string.wifi_empty_list_user_restricted); 834 } 835 return; 836 } 837 838 final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off); 839 840 // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead, 841 // read the system settings directly. Because when the device is in Airplane mode, even if 842 // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off". 843 final ContentResolver resolver = getActivity().getContentResolver(); 844 final boolean wifiScanningMode = Settings.Global.getInt( 845 resolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1; 846 847 if (!wifiScanningMode) { 848 // Show only the brief text if the user is not allowed to configure scanning settings, 849 // or the scanning mode has been turned off. 850 mStatusMessagePreference.setTitle(briefText); 851 } else { 852 LinkifyUtils.OnClickListener clickListener = new LinkifyUtils.OnClickListener() { 853 @Override 854 public void onClick() { 855 final SettingsActivity activity = (SettingsActivity) getActivity(); 856 activity.startPreferencePanel(WifiSettings.this, 857 ScanningSettings.class.getName(), 858 null, R.string.location_scanning_screen_title, null, null, 0); 859 } 860 }; 861 mStatusMessagePreference.setText( 862 briefText, getText(R.string.wifi_scan_notify_text), clickListener); 863 } 864 removeConnectedAccessPointPreference(); 865 mAccessPointsPreferenceCategory.removeAll(); 866 mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference); 867 } 868 869 private void addMessagePreference(int messageId) { 870 mStatusMessagePreference.setTitle(messageId); 871 removeConnectedAccessPointPreference(); 872 mAccessPointsPreferenceCategory.removeAll(); 873 mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference); 874 } 875 876 protected void setProgressBarVisible(boolean visible) { 877 if (mProgressHeader != null) { 878 mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); 879 } 880 } 881 882 @Override 883 public void onWifiStateChanged(int state) { 884 switch (state) { 885 case WifiManager.WIFI_STATE_ENABLING: 886 addMessagePreference(R.string.wifi_starting); 887 setProgressBarVisible(true); 888 break; 889 890 case WifiManager.WIFI_STATE_DISABLED: 891 setOffMessage(); 892 setProgressBarVisible(false); 893 break; 894 } 895 } 896 897 @Override 898 public void onConnectedChanged() { 899 changeNextButtonState(mWifiTracker.isConnected()); 900 } 901 902 /** 903 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 904 * Wifi setup screens, not in usual wifi settings screen. 905 * 906 * @param enabled true when the device is connected to a wifi network. 907 */ 908 private void changeNextButtonState(boolean enabled) { 909 if (mEnableNextOnConnection && hasNextButton()) { 910 getNextButton().setEnabled(enabled); 911 } 912 } 913 914 @Override 915 public void onForget(WifiDialog dialog) { 916 forget(); 917 } 918 919 @Override 920 public void onSubmit(WifiDialog dialog) { 921 if (mDialog != null) { 922 submit(mDialog.getController()); 923 } 924 } 925 926 /* package */ void submit(WifiConfigController configController) { 927 928 final WifiConfiguration config = configController.getConfig(); 929 930 if (config == null) { 931 if (mSelectedAccessPoint != null 932 && mSelectedAccessPoint.isSaved()) { 933 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */); 934 } 935 } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) { 936 mWifiManager.save(config, mSaveListener); 937 } else { 938 mWifiManager.save(config, mSaveListener); 939 if (mSelectedAccessPoint != null) { // Not an "Add network" 940 connect(config, false /* isSavedNetwork */); 941 } 942 } 943 944 mWifiTracker.resumeScanning(); 945 } 946 947 /* package */ void forget() { 948 mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_FORGET); 949 if (!mSelectedAccessPoint.isSaved()) { 950 if (mSelectedAccessPoint.getNetworkInfo() != null && 951 mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) { 952 // Network is active but has no network ID - must be ephemeral. 953 mWifiManager.disableEphemeralNetwork( 954 AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr())); 955 } else { 956 // Should not happen, but a monkey seems to trigger it 957 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 958 return; 959 } 960 } else if (mSelectedAccessPoint.getConfig().isPasspoint()) { 961 mWifiManager.removePasspointConfiguration(mSelectedAccessPoint.getConfig().FQDN); 962 } else { 963 mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener); 964 } 965 966 mWifiTracker.resumeScanning(); 967 968 // We need to rename/replace "Next" button in wifi setup context. 969 changeNextButtonState(false); 970 } 971 972 protected void connect(final WifiConfiguration config, boolean isSavedNetwork) { 973 // Log subtype if configuration is a saved network. 974 mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT, 975 isSavedNetwork); 976 mWifiManager.connect(config, mConnectListener); 977 } 978 979 protected void connect(final int networkId, boolean isSavedNetwork) { 980 // Log subtype if configuration is a saved network. 981 mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT, 982 isSavedNetwork); 983 mWifiManager.connect(networkId, mConnectListener); 984 } 985 986 /** 987 * Called when "add network" button is pressed. 988 */ 989 /* package */ void onAddNetworkPressed() { 990 mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_ADD_NETWORK); 991 // No exact access point is selected. 992 mSelectedAccessPoint = null; 993 showDialog(null, WifiConfigUiBase.MODE_CONNECT); 994 } 995 996 @Override 997 protected int getHelpResource() { 998 return R.string.help_url_wifi; 999 } 1000 1001 @Override 1002 public void onAccessPointChanged(final AccessPoint accessPoint) { 1003 View view = getView(); 1004 if (view != null) { 1005 view.post(new Runnable() { 1006 @Override 1007 public void run() { 1008 Object tag = accessPoint.getTag(); 1009 if (tag != null) { 1010 ((LongPressAccessPointPreference) tag).refresh(); 1011 } 1012 } 1013 }); 1014 } 1015 } 1016 1017 @Override 1018 public void onLevelChanged(AccessPoint accessPoint) { 1019 ((LongPressAccessPointPreference) accessPoint.getTag()).onLevelChanged(); 1020 } 1021 1022 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1023 new BaseSearchIndexProvider() { 1024 @Override 1025 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 1026 final List<SearchIndexableRaw> result = new ArrayList<>(); 1027 final Resources res = context.getResources(); 1028 1029 // Add fragment title 1030 SearchIndexableRaw data = new SearchIndexableRaw(context); 1031 data.title = res.getString(R.string.wifi_settings); 1032 data.screenTitle = res.getString(R.string.wifi_settings); 1033 data.keywords = res.getString(R.string.keywords_wifi); 1034 data.key = DATA_KEY_REFERENCE; 1035 result.add(data); 1036 1037 // Add saved Wi-Fi access points 1038 final List<AccessPoint> accessPoints = 1039 WifiTracker.getCurrentAccessPoints(context, true, false, false); 1040 for (AccessPoint accessPoint : accessPoints) { 1041 data = new SearchIndexableRaw(context); 1042 data.title = accessPoint.getSsidStr(); 1043 data.screenTitle = res.getString(R.string.wifi_settings); 1044 data.enabled = enabled; 1045 result.add(data); 1046 } 1047 1048 return result; 1049 } 1050 }; 1051 1052 /** 1053 * Returns true if the config is not editable through Settings. 1054 * @param context Context of caller 1055 * @param config The WiFi config. 1056 * @return true if the config is not editable through Settings. 1057 */ 1058 static boolean isEditabilityLockedDown(Context context, WifiConfiguration config) { 1059 return !canModifyNetwork(context, config); 1060 } 1061 1062 /** 1063 * This method is a stripped version of WifiConfigStore.canModifyNetwork. 1064 * TODO: refactor to have only one method. 1065 * @param context Context of caller 1066 * @param config The WiFi config. 1067 * @return true if Settings can modify the config. 1068 */ 1069 static boolean canModifyNetwork(Context context, WifiConfiguration config) { 1070 if (config == null) { 1071 return true; 1072 } 1073 1074 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 1075 Context.DEVICE_POLICY_SERVICE); 1076 1077 // Check if device has DPM capability. If it has and dpm is still null, then we 1078 // treat this case with suspicion and bail out. 1079 final PackageManager pm = context.getPackageManager(); 1080 if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) { 1081 return false; 1082 } 1083 1084 boolean isConfigEligibleForLockdown = false; 1085 if (dpm != null) { 1086 final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); 1087 if (deviceOwner != null) { 1088 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); 1089 try { 1090 final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(), 1091 deviceOwnerUserId); 1092 isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid; 1093 } catch (NameNotFoundException e) { 1094 // don't care 1095 } 1096 } 1097 } 1098 if (!isConfigEligibleForLockdown) { 1099 return true; 1100 } 1101 1102 final ContentResolver resolver = context.getContentResolver(); 1103 final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, 1104 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; 1105 return !isLockdownFeatureEnabled; 1106 } 1107 1108 private static class SummaryProvider 1109 implements SummaryLoader.SummaryProvider, OnSummaryChangeListener { 1110 1111 private final Context mContext; 1112 private final SummaryLoader mSummaryLoader; 1113 1114 @VisibleForTesting 1115 WifiSummaryUpdater mSummaryHelper; 1116 1117 public SummaryProvider(Context context, SummaryLoader summaryLoader) { 1118 mContext = context; 1119 mSummaryLoader = summaryLoader; 1120 mSummaryHelper = new WifiSummaryUpdater(mContext, this); 1121 } 1122 1123 1124 @Override 1125 public void setListening(boolean listening) { 1126 mSummaryHelper.register(listening); 1127 } 1128 1129 @Override 1130 public void onSummaryChanged(String summary) { 1131 mSummaryLoader.setSummary(this, summary); 1132 } 1133 } 1134 1135 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 1136 = new SummaryLoader.SummaryProviderFactory() { 1137 @Override 1138 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 1139 SummaryLoader summaryLoader) { 1140 return new SummaryProvider(activity, summaryLoader); 1141 } 1142 }; 1143} 1144