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