WifiSettings.java revision cb5444ad2ccf2f16bddafb8844cde6a1a92adddb
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.AlertDialog; 24import android.app.AppGlobals; 25import android.app.Dialog; 26import android.app.admin.DevicePolicyManager; 27import android.content.Context; 28import android.content.DialogInterface; 29import android.content.Intent; 30import android.content.pm.ApplicationInfo; 31import android.content.pm.IPackageManager; 32import android.content.pm.PackageManager; 33import android.content.pm.PackageManager.NameNotFoundException; 34import android.content.res.Resources; 35import android.content.res.TypedArray; 36import android.net.ConnectivityManager; 37import android.net.NetworkInfo; 38import android.net.NetworkInfo.State; 39import android.net.wifi.WifiConfiguration; 40import android.net.wifi.WifiManager; 41import android.net.wifi.WpsInfo; 42import android.nfc.NfcAdapter; 43import android.os.Bundle; 44import android.os.RemoteException; 45import android.os.UserHandle; 46import android.os.UserManager; 47import android.preference.Preference; 48import android.preference.PreferenceScreen; 49import android.text.Spannable; 50import android.text.style.TextAppearanceSpan; 51import android.util.Log; 52import android.view.ContextMenu; 53import android.view.ContextMenu.ContextMenuInfo; 54import android.view.Gravity; 55import android.view.Menu; 56import android.view.MenuInflater; 57import android.view.MenuItem; 58import android.view.View; 59import android.widget.AdapterView.AdapterContextMenuInfo; 60import android.widget.ProgressBar; 61import android.widget.TextView; 62import android.widget.TextView.BufferType; 63import android.widget.Toast; 64 65import com.android.internal.logging.MetricsLogger; 66import com.android.settings.LinkifyUtils; 67import com.android.settings.R; 68import com.android.settings.RestrictedSettingsFragment; 69import com.android.settings.SettingsActivity; 70import com.android.settings.location.ScanningSettings; 71import com.android.settings.search.BaseSearchIndexProvider; 72import com.android.settings.search.Indexable; 73import com.android.settings.search.SearchIndexableRaw; 74import com.android.settingslib.wifi.AccessPoint; 75import com.android.settingslib.wifi.AccessPoint.AccessPointListener; 76import com.android.settingslib.wifi.WifiTracker; 77 78import java.util.ArrayList; 79import java.util.Collection; 80import java.util.List; 81 82/** 83 * Two types of UI are provided here. 84 * 85 * The first is for "usual Settings", appearing as any other Setup fragment. 86 * 87 * The second is for Setup Wizard, with a simplified interface that hides the action bar 88 * and menus. 89 */ 90public class WifiSettings extends RestrictedSettingsFragment 91 implements DialogInterface.OnClickListener, Indexable, WifiTracker.WifiListener, 92 AccessPointListener { 93 94 private static final String TAG = "WifiSettings"; 95 96 /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST; 97 private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; 98 private static final int MENU_ID_SAVED_NETWORK = Menu.FIRST + 2; 99 /* package */ static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3; 100 private static final int MENU_ID_ADVANCED = Menu.FIRST + 4; 101 private static final int MENU_ID_SCAN = Menu.FIRST + 5; 102 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 103 private static final int MENU_ID_FORGET = Menu.FIRST + 7; 104 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 105 private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9; 106 107 public static final int WIFI_DIALOG_ID = 1; 108 /* package */ static final int WPS_PBC_DIALOG_ID = 2; 109 private static final int WPS_PIN_DIALOG_ID = 3; 110 private static final int WRITE_NFC_DIALOG_ID = 6; 111 112 // Instance state keys 113 private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode"; 114 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 115 private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state"; 116 117 private static boolean savedNetworksExist; 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 TextView mEmptyView; 132 private ProgressBar mProgressHeader; 133 134 // this boolean extra specifies whether to disable the Next button when not connected. Used by 135 // account creation outside of setup wizard. 136 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 137 // This string extra specifies a network to open the connect dialog on, so the user can enter 138 // network credentials. This is used by quick settings for secured networks. 139 private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 140 141 // should Next button only be enabled when we have a connection? 142 private boolean mEnableNextOnConnection; 143 144 // Save the dialog details 145 private boolean mDlgEdit; 146 private AccessPoint mDlgAccessPoint; 147 private Bundle mAccessPointSavedState; 148 private Bundle mWifiNfcDialogSavedState; 149 150 private WifiTracker mWifiTracker; 151 private String mOpenSsid; 152 153 /* End of "used in Wifi Setup context" */ 154 155 public WifiSettings() { 156 super(DISALLOW_CONFIG_WIFI); 157 } 158 159 @Override 160 public void onViewCreated(View view, Bundle savedInstanceState) { 161 super.onViewCreated(view, savedInstanceState); 162 final Activity activity = getActivity(); 163 if (activity != null) { 164 mProgressHeader = (ProgressBar) setPinnedHeaderView(R.layout.wifi_progress_header); 165 } 166 } 167 168 @Override 169 public void onActivityCreated(Bundle savedInstanceState) { 170 super.onActivityCreated(savedInstanceState); 171 172 mWifiTracker = new WifiTracker(getActivity(), this, true, true, false); 173 mWifiManager = mWifiTracker.getManager(); 174 175 mConnectListener = new WifiManager.ActionListener() { 176 @Override 177 public void onSuccess() { 178 } 179 @Override 180 public void onFailure(int reason) { 181 Activity activity = getActivity(); 182 if (activity != null) { 183 Toast.makeText(activity, 184 R.string.wifi_failed_connect_message, 185 Toast.LENGTH_SHORT).show(); 186 } 187 } 188 }; 189 190 mSaveListener = new WifiManager.ActionListener() { 191 @Override 192 public void onSuccess() { 193 } 194 @Override 195 public void onFailure(int reason) { 196 Activity activity = getActivity(); 197 if (activity != null) { 198 Toast.makeText(activity, 199 R.string.wifi_failed_save_message, 200 Toast.LENGTH_SHORT).show(); 201 } 202 } 203 }; 204 205 mForgetListener = new WifiManager.ActionListener() { 206 @Override 207 public void onSuccess() { 208 } 209 @Override 210 public void onFailure(int reason) { 211 Activity activity = getActivity(); 212 if (activity != null) { 213 Toast.makeText(activity, 214 R.string.wifi_failed_forget_message, 215 Toast.LENGTH_SHORT).show(); 216 } 217 } 218 }; 219 220 if (savedInstanceState != null) { 221 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); 222 if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 223 mAccessPointSavedState = 224 savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 225 } 226 227 if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) { 228 mWifiNfcDialogSavedState = 229 savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE); 230 } 231 } 232 233 // if we're supposed to enable/disable the Next button based on our current connection 234 // state, start it off in the right state 235 Intent intent = getActivity().getIntent(); 236 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 237 238 if (mEnableNextOnConnection) { 239 if (hasNextButton()) { 240 final ConnectivityManager connectivity = (ConnectivityManager) 241 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 242 if (connectivity != null) { 243 NetworkInfo info = connectivity.getNetworkInfo( 244 ConnectivityManager.TYPE_WIFI); 245 changeNextButtonState(info.isConnected()); 246 } 247 } 248 } 249 250 addPreferencesFromResource(R.xml.wifi_settings); 251 252 mEmptyView = initEmptyView(); 253 registerForContextMenu(getListView()); 254 setHasOptionsMenu(true); 255 256 if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) { 257 mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID); 258 onAccessPointsChanged(); 259 } 260 } 261 262 @Override 263 public void onDestroyView() { 264 super.onDestroyView(); 265 266 if (mWifiEnabler != null) { 267 mWifiEnabler.teardownSwitchBar(); 268 } 269 } 270 271 @Override 272 public void onStart() { 273 super.onStart(); 274 275 // On/off switch is hidden for Setup Wizard (returns null) 276 mWifiEnabler = createWifiEnabler(); 277 } 278 279 /** 280 * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard) 281 */ 282 /* package */ WifiEnabler createWifiEnabler() { 283 final SettingsActivity activity = (SettingsActivity) getActivity(); 284 return new WifiEnabler(activity, activity.getSwitchBar()); 285 } 286 287 @Override 288 public void onResume() { 289 final Activity activity = getActivity(); 290 super.onResume(); 291 if (mWifiEnabler != null) { 292 mWifiEnabler.resume(activity); 293 } 294 295 mWifiTracker.startTracking(); 296 } 297 298 @Override 299 public void onPause() { 300 super.onPause(); 301 if (mWifiEnabler != null) { 302 mWifiEnabler.pause(); 303 } 304 305 mWifiTracker.stopTracking(); 306 } 307 308 @Override 309 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 310 // If the user is not allowed to configure wifi, do not show the menu. 311 if (isUiRestricted()) return; 312 313 addOptionsMenuItems(menu); 314 super.onCreateOptionsMenu(menu, inflater); 315 } 316 317 /** 318 * @param menu 319 */ 320 void addOptionsMenuItems(Menu menu) { 321 final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled(); 322 TypedArray ta = getActivity().getTheme().obtainStyledAttributes( 323 new int[] {R.attr.ic_menu_add, R.attr.ic_wps}); 324 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 325 .setIcon(ta.getDrawable(0)) 326 .setEnabled(wifiIsEnabled) 327 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 328 if (savedNetworksExist) { 329 menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label) 330 .setIcon(ta.getDrawable(0)) 331 .setEnabled(wifiIsEnabled) 332 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 333 } 334 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh) 335 .setEnabled(wifiIsEnabled) 336 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 337 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 338 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 339 ta.recycle(); 340 } 341 342 @Override 343 protected int getMetricsCategory() { 344 return MetricsLogger.WIFI; 345 } 346 347 @Override 348 public void onSaveInstanceState(Bundle outState) { 349 super.onSaveInstanceState(outState); 350 351 // If the dialog is showing, save its state. 352 if (mDialog != null && mDialog.isShowing()) { 353 outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit); 354 if (mDlgAccessPoint != null) { 355 mAccessPointSavedState = new Bundle(); 356 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 357 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 358 } 359 } 360 361 if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) { 362 Bundle savedState = new Bundle(); 363 mWifiToNfcDialog.saveState(savedState); 364 outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState); 365 } 366 } 367 368 @Override 369 public boolean onOptionsItemSelected(MenuItem item) { 370 // If the user is not allowed to configure wifi, do not handle menu selections. 371 if (isUiRestricted()) return false; 372 373 switch (item.getItemId()) { 374 case MENU_ID_WPS_PBC: 375 showDialog(WPS_PBC_DIALOG_ID); 376 return true; 377 /* 378 case MENU_ID_P2P: 379 if (getActivity() instanceof SettingsActivity) { 380 ((SettingsActivity) getActivity()).startPreferencePanel( 381 WifiP2pSettings.class.getCanonicalName(), 382 null, 383 R.string.wifi_p2p_settings_title, null, 384 this, 0); 385 } else { 386 startFragment(this, WifiP2pSettings.class.getCanonicalName(), 387 R.string.wifi_p2p_settings_title, -1, null); 388 } 389 return true; 390 */ 391 case MENU_ID_WPS_PIN: 392 showDialog(WPS_PIN_DIALOG_ID); 393 return true; 394 case MENU_ID_SCAN: 395 MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORCE_SCAN); 396 mWifiTracker.forceScan(); 397 return true; 398 case MENU_ID_ADD_NETWORK: 399 if (mWifiTracker.isWifiEnabled()) { 400 onAddNetworkPressed(); 401 } 402 return true; 403 case MENU_ID_SAVED_NETWORK: 404 if (getActivity() instanceof SettingsActivity) { 405 ((SettingsActivity) getActivity()).startPreferencePanel( 406 SavedAccessPointsWifiSettings.class.getCanonicalName(), null, 407 R.string.wifi_saved_access_points_titlebar, null, this, 0); 408 } else { 409 startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(), 410 R.string.wifi_saved_access_points_titlebar, 411 -1 /* Do not request a result */, null); 412 } 413 return true; 414 case MENU_ID_ADVANCED: 415 if (getActivity() instanceof SettingsActivity) { 416 ((SettingsActivity) getActivity()).startPreferencePanel( 417 AdvancedWifiSettings.class.getCanonicalName(), null, 418 R.string.wifi_advanced_titlebar, null, this, 0); 419 } else { 420 startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), 421 R.string.wifi_advanced_titlebar, -1 /* Do not request a results */, 422 null); 423 } 424 return true; 425 } 426 return super.onOptionsItemSelected(item); 427 } 428 429 @Override 430 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 431 if (info instanceof AdapterContextMenuInfo) { 432 Preference preference = (Preference) getListView().getItemAtPosition( 433 ((AdapterContextMenuInfo) info).position); 434 435 if (preference instanceof AccessPointPreference) { 436 mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint(); 437 menu.setHeaderTitle(mSelectedAccessPoint.getSsid()); 438 if (mSelectedAccessPoint.isConnectable()) { 439 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 440 } 441 442 WifiConfiguration config = mSelectedAccessPoint.getConfig(); 443 // Some configs are ineditable 444 if (isEditabilityLockedDown(getActivity(), config)) { 445 return; 446 } 447 448 if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) { 449 // Allow forgetting a network if either the network is saved or ephemerally 450 // connected. (In the latter case, "forget" blacklists the network so it won't 451 // be used again, ephemerally). 452 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 453 } 454 if (mSelectedAccessPoint.isSaved()) { 455 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 456 NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); 457 if (nfcAdapter != null && nfcAdapter.isEnabled() && 458 mSelectedAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { 459 // Only allow writing of NFC tags for password-protected networks. 460 menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc); 461 } 462 } 463 } 464 } 465 } 466 467 @Override 468 public boolean onContextItemSelected(MenuItem item) { 469 if (mSelectedAccessPoint == null) { 470 return super.onContextItemSelected(item); 471 } 472 switch (item.getItemId()) { 473 case MENU_ID_CONNECT: { 474 if (mSelectedAccessPoint.isSaved()) { 475 connect(mSelectedAccessPoint.getConfig()); 476 } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) { 477 /** Bypass dialog for unsecured networks */ 478 mSelectedAccessPoint.generateOpenNetworkConfig(); 479 connect(mSelectedAccessPoint.getConfig()); 480 } else { 481 showDialog(mSelectedAccessPoint, true); 482 } 483 return true; 484 } 485 case MENU_ID_FORGET: { 486 forget(); 487 return true; 488 } 489 case MENU_ID_MODIFY: { 490 showDialog(mSelectedAccessPoint, true); 491 return true; 492 } 493 case MENU_ID_WRITE_NFC: 494 showDialog(WRITE_NFC_DIALOG_ID); 495 return true; 496 497 } 498 return super.onContextItemSelected(item); 499 } 500 501 @Override 502 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 503 if (preference instanceof AccessPointPreference) { 504 mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint(); 505 /** Bypass dialog for unsecured, unsaved, and inactive networks */ 506 if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE && 507 !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) { 508 mSelectedAccessPoint.generateOpenNetworkConfig(); 509 if (!savedNetworksExist) { 510 savedNetworksExist = true; 511 getActivity().invalidateOptionsMenu(); 512 } 513 connect(mSelectedAccessPoint.getConfig()); 514 } else { 515 showDialog(mSelectedAccessPoint, false); 516 } 517 } else { 518 return super.onPreferenceTreeClick(screen, preference); 519 } 520 return true; 521 } 522 523 private void showDialog(AccessPoint accessPoint, boolean edit) { 524 if (accessPoint != null) { 525 WifiConfiguration config = accessPoint.getConfig(); 526 if (isEditabilityLockedDown(getActivity(), config) && accessPoint.isActive()) { 527 final int userId = UserHandle.getUserId(config.creatorUid); 528 final PackageManager pm = getActivity().getPackageManager(); 529 final IPackageManager ipm = AppGlobals.getPackageManager(); 530 String appName = pm.getNameForUid(config.creatorUid); 531 try { 532 final ApplicationInfo appInfo = ipm.getApplicationInfo(appName, /* flags */ 0, 533 userId); 534 final CharSequence label = pm.getApplicationLabel(appInfo); 535 if (label != null) { 536 appName = label.toString(); 537 } 538 } catch (RemoteException e) { 539 // leave appName as packageName 540 } 541 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 542 builder.setTitle(accessPoint.getSsid()) 543 .setMessage(getString(R.string.wifi_alert_lockdown_by_device_owner, 544 appName)) 545 .setPositiveButton(android.R.string.ok, null) 546 .show(); 547 return; 548 } 549 } 550 551 if (mDialog != null) { 552 removeDialog(WIFI_DIALOG_ID); 553 mDialog = null; 554 } 555 556 // Save the access point and edit mode 557 mDlgAccessPoint = accessPoint; 558 mDlgEdit = edit; 559 560 showDialog(WIFI_DIALOG_ID); 561 } 562 563 @Override 564 public Dialog onCreateDialog(int dialogId) { 565 switch (dialogId) { 566 case WIFI_DIALOG_ID: 567 AccessPoint ap = mDlgAccessPoint; // For manual launch 568 if (ap == null) { // For re-launch from saved state 569 if (mAccessPointSavedState != null) { 570 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 571 // For repeated orientation changes 572 mDlgAccessPoint = ap; 573 // Reset the saved access point data 574 mAccessPointSavedState = null; 575 } 576 } 577 // If it's null, fine, it's for Add Network 578 mSelectedAccessPoint = ap; 579 final boolean hideForget = (ap == null || isEditabilityLockedDown(getActivity(), 580 ap.getConfig())); 581 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit, 582 /* no hide submit/connect */ false, 583 /* hide forget if config locked down */ hideForget); 584 return mDialog; 585 case WPS_PBC_DIALOG_ID: 586 return new WpsDialog(getActivity(), WpsInfo.PBC); 587 case WPS_PIN_DIALOG_ID: 588 return new WpsDialog(getActivity(), WpsInfo.DISPLAY); 589 case WRITE_NFC_DIALOG_ID: 590 if (mSelectedAccessPoint != null) { 591 mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( 592 getActivity(), mSelectedAccessPoint.getConfig().networkId, 593 mSelectedAccessPoint.getSecurity(), 594 mWifiManager); 595 } else if (mWifiNfcDialogSavedState != null) { 596 mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( 597 getActivity(), mWifiNfcDialogSavedState, mWifiManager); 598 } 599 600 return mWifiToNfcDialog; 601 } 602 return super.onCreateDialog(dialogId); 603 } 604 605 /** 606 * Shows the latest access points available with supplemental information like 607 * the strength of network and the security for it. 608 */ 609 @Override 610 public void onAccessPointsChanged() { 611 // Safeguard from some delayed event handling 612 if (getActivity() == null) return; 613 614 if (isUiRestricted()) { 615 addMessagePreference(R.string.wifi_empty_list_user_restricted); 616 return; 617 } 618 final int wifiState = mWifiManager.getWifiState(); 619 620 switch (wifiState) { 621 case WifiManager.WIFI_STATE_ENABLED: 622 // AccessPoints are automatically sorted with TreeSet. 623 final Collection<AccessPoint> accessPoints = 624 mWifiTracker.getAccessPoints(); 625 getPreferenceScreen().removeAll(); 626 627 boolean hasAvailableAccessPoints = false; 628 for (AccessPoint accessPoint : accessPoints) { 629 // Ignore access points that are out of range. 630 if (accessPoint.getLevel() != -1) { 631 hasAvailableAccessPoints = true; 632 AccessPointPreference preference = new AccessPointPreference(accessPoint, 633 getActivity(), false); 634 635 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsid()) 636 && !accessPoint.isSaved() 637 && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { 638 onPreferenceTreeClick(getPreferenceScreen(), preference); 639 mOpenSsid = null; 640 } 641 getPreferenceScreen().addPreference(preference); 642 accessPoint.setListener(this); 643 } 644 } 645 if (!hasAvailableAccessPoints) { 646 setProgressBarVisible(true); 647 addMessagePreference(R.string.wifi_empty_list_wifi_on); 648 } else { 649 setProgressBarVisible(false); 650 } 651 break; 652 653 case WifiManager.WIFI_STATE_ENABLING: 654 getPreferenceScreen().removeAll(); 655 setProgressBarVisible(true); 656 break; 657 658 case WifiManager.WIFI_STATE_DISABLING: 659 addMessagePreference(R.string.wifi_stopping); 660 setProgressBarVisible(true); 661 break; 662 663 case WifiManager.WIFI_STATE_DISABLED: 664 setOffMessage(); 665 setProgressBarVisible(false); 666 break; 667 } 668 // Update "Saved Networks" menu option. 669 if (savedNetworksExist != mWifiTracker.doSavedNetworksExist()) { 670 savedNetworksExist = !savedNetworksExist; 671 getActivity().invalidateOptionsMenu(); 672 } 673 } 674 675 protected TextView initEmptyView() { 676 TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty); 677 emptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); 678 getListView().setEmptyView(emptyView); 679 return emptyView; 680 } 681 682 private void setOffMessage() { 683 if (mEmptyView == null) { 684 return; 685 } 686 687 final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off); 688 if (isUiRestricted()) { 689 // Show only the brief text if the user is not allowed to configure scanning settings. 690 mEmptyView.setText(briefText, BufferType.SPANNABLE); 691 } else { 692 // Append the description of scanning settings with link. 693 final StringBuilder contentBuilder = new StringBuilder(); 694 contentBuilder.append(briefText); 695 contentBuilder.append("\n\n"); 696 contentBuilder.append(getText(R.string.wifi_scan_notify_text)); 697 LinkifyUtils.linkify(mEmptyView, contentBuilder, new LinkifyUtils.OnClickListener() { 698 @Override 699 public void onClick() { 700 final SettingsActivity activity = 701 (SettingsActivity) WifiSettings.this.getActivity(); 702 activity.startPreferencePanel(ScanningSettings.class.getName(), null, 703 R.string.location_scanning_screen_title, null, null, 0); 704 } 705 }); 706 } 707 // Embolden and enlarge the brief description anyway. 708 Spannable boldSpan = (Spannable) mEmptyView.getText(); 709 boldSpan.setSpan( 710 new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0, 711 briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 712 getPreferenceScreen().removeAll(); 713 } 714 715 private void addMessagePreference(int messageId) { 716 if (mEmptyView != null) mEmptyView.setText(messageId); 717 getPreferenceScreen().removeAll(); 718 } 719 720 protected void setProgressBarVisible(boolean visible) { 721 if (mProgressHeader != null) { 722 mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); 723 } 724 } 725 726 @Override 727 public void onWifiStateChanged(int state) { 728 Activity activity = getActivity(); 729 if (activity != null) { 730 activity.invalidateOptionsMenu(); 731 } 732 733 switch (state) { 734 case WifiManager.WIFI_STATE_ENABLING: 735 addMessagePreference(R.string.wifi_starting); 736 setProgressBarVisible(true); 737 break; 738 739 case WifiManager.WIFI_STATE_DISABLED: 740 setOffMessage(); 741 setProgressBarVisible(false); 742 break; 743 } 744 } 745 746 @Override 747 public void onConnectedChanged() { 748 changeNextButtonState(mWifiTracker.isConnected()); 749 } 750 751 /** 752 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 753 * Wifi setup screens, not in usual wifi settings screen. 754 * 755 * @param enabled true when the device is connected to a wifi network. 756 */ 757 private void changeNextButtonState(boolean enabled) { 758 if (mEnableNextOnConnection && hasNextButton()) { 759 getNextButton().setEnabled(enabled); 760 } 761 } 762 763 @Override 764 public void onClick(DialogInterface dialogInterface, int button) { 765 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 766 forget(); 767 } else if (button == WifiDialog.BUTTON_SUBMIT) { 768 if (mDialog != null) { 769 submit(mDialog.getController()); 770 } 771 } 772 } 773 774 /* package */ void submit(WifiConfigController configController) { 775 776 final WifiConfiguration config = configController.getConfig(); 777 778 if (config == null) { 779 if (mSelectedAccessPoint != null 780 && mSelectedAccessPoint.isSaved()) { 781 connect(mSelectedAccessPoint.getConfig()); 782 } 783 } else if (config.networkId != INVALID_NETWORK_ID) { 784 if (mSelectedAccessPoint != null) { 785 mWifiManager.save(config, mSaveListener); 786 } 787 } else { 788 if (configController.isEdit()) { 789 mWifiManager.save(config, mSaveListener); 790 } else { 791 connect(config); 792 } 793 } 794 795 mWifiTracker.resumeScanning(); 796 } 797 798 /* package */ void forget() { 799 MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORGET); 800 if (!mSelectedAccessPoint.isSaved()) { 801 if (mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) { 802 // Network is active but has no network ID - must be ephemeral. 803 mWifiManager.disableEphemeralNetwork( 804 AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsid())); 805 } else { 806 // Should not happen, but a monkey seems to trigger it 807 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 808 return; 809 } 810 } else { 811 mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener); 812 } 813 814 mWifiTracker.resumeScanning(); 815 816 // We need to rename/replace "Next" button in wifi setup context. 817 changeNextButtonState(false); 818 } 819 820 protected void connect(final WifiConfiguration config) { 821 MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT); 822 mWifiManager.connect(config, mConnectListener); 823 } 824 825 protected void connect(final int networkId) { 826 MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT); 827 mWifiManager.connect(networkId, mConnectListener); 828 } 829 830 /** 831 * Refreshes acccess points and ask Wifi module to scan networks again. 832 */ 833 /* package */ void refreshAccessPoints() { 834 mWifiTracker.resumeScanning(); 835 836 getPreferenceScreen().removeAll(); 837 } 838 839 /** 840 * Called when "add network" button is pressed. 841 */ 842 /* package */ void onAddNetworkPressed() { 843 MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_ADD_NETWORK); 844 // No exact access point is selected. 845 mSelectedAccessPoint = null; 846 showDialog(null, true); 847 } 848 849 /* package */ int getAccessPointsCount() { 850 final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled(); 851 if (wifiIsEnabled) { 852 return getPreferenceScreen().getPreferenceCount(); 853 } else { 854 return 0; 855 } 856 } 857 858 /** 859 * Requests wifi module to pause wifi scan. May be ignored when the module is disabled. 860 */ 861 /* package */ void pauseWifiScan() { 862 mWifiTracker.pauseScanning(); 863 } 864 865 /** 866 * Requests wifi module to resume wifi scan. May be ignored when the module is disabled. 867 */ 868 /* package */ void resumeWifiScan() { 869 mWifiTracker.resumeScanning(); 870 } 871 872 @Override 873 protected int getHelpResource() { 874 return R.string.help_url_wifi; 875 } 876 877 @Override 878 public void onAccessPointChanged(AccessPoint accessPoint) { 879 ((AccessPointPreference) accessPoint.getTag()).refresh(); 880 } 881 882 @Override 883 public void onLevelChanged(AccessPoint accessPoint) { 884 ((AccessPointPreference) accessPoint.getTag()).onLevelChanged(); 885 } 886 887 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 888 new BaseSearchIndexProvider() { 889 @Override 890 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 891 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); 892 final Resources res = context.getResources(); 893 894 // Add fragment title 895 SearchIndexableRaw data = new SearchIndexableRaw(context); 896 data.title = res.getString(R.string.wifi_settings); 897 data.screenTitle = res.getString(R.string.wifi_settings); 898 data.keywords = res.getString(R.string.keywords_wifi); 899 result.add(data); 900 901 // Add saved Wi-Fi access points 902 final Collection<AccessPoint> accessPoints = 903 WifiTracker.getCurrentAccessPoints(context, true, false, false); 904 for (AccessPoint accessPoint : accessPoints) { 905 data = new SearchIndexableRaw(context); 906 data.title = accessPoint.getSsid(); 907 data.screenTitle = res.getString(R.string.wifi_settings); 908 data.enabled = enabled; 909 result.add(data); 910 } 911 912 return result; 913 } 914 }; 915 916 /** 917 * Returns true if the config is not editable/removable except by its creating Device Owner. 918 * @param config The WiFi config. 919 * @return true if the config is not editable/removable except by its creating Device Owner. 920 */ 921 static boolean isEditabilityLockedDown(Context context, WifiConfiguration config) { 922 if (config == null) { 923 return false; 924 } 925 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 926 Context.DEVICE_POLICY_SERVICE); 927 final String deviceOwnerPackageName = dpm.getDeviceOwner(); 928 if (deviceOwnerPackageName == null) { 929 return false; 930 } 931 UserManager um = UserManager.get(context); 932 if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) { 933 return false; 934 } 935 final PackageManager pm = context.getPackageManager(); 936 try { 937 final int deviceOwnerUid = pm.getPackageUid(deviceOwnerPackageName, 938 UserHandle.getUserId(config.creatorUid)); 939 return deviceOwnerUid == config.creatorUid; 940 } catch (NameNotFoundException e) { 941 return false; 942 } 943 } 944} 945