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