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