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