WifiSettings.java revision 58d6ffc5f56a94afade76ac8dc434c6e3d43fd6d
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; 20 21import android.app.ActionBar; 22import android.app.Activity; 23import android.app.Dialog; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.pm.PackageManager; 30import android.content.res.Resources; 31import android.net.ConnectivityManager; 32import android.net.NetworkInfo; 33import android.net.NetworkInfo.DetailedState; 34import android.net.wifi.ScanResult; 35import android.net.wifi.SupplicantState; 36import android.net.wifi.WifiConfiguration; 37import android.net.wifi.WifiInfo; 38import android.net.wifi.WifiManager; 39import android.net.wifi.WpsInfo; 40import android.os.Bundle; 41import android.os.Handler; 42import android.os.Message; 43import android.preference.Preference; 44import android.preference.PreferenceActivity; 45import android.preference.PreferenceScreen; 46import android.security.Credentials; 47import android.security.KeyStore; 48import android.util.AttributeSet; 49import android.util.Log; 50import android.view.ContextMenu; 51import android.view.ContextMenu.ContextMenuInfo; 52import android.view.Gravity; 53import android.view.LayoutInflater; 54import android.view.Menu; 55import android.view.MenuInflater; 56import android.view.MenuItem; 57import android.view.View; 58import android.view.View.OnClickListener; 59import android.view.ViewGroup; 60import android.widget.AdapterView.AdapterContextMenuInfo; 61import android.widget.ImageButton; 62import android.widget.PopupMenu; 63import android.widget.PopupMenu.OnMenuItemClickListener; 64import android.widget.RelativeLayout; 65import android.widget.Switch; 66import android.widget.TextView; 67import android.widget.Toast; 68 69import com.android.settings.R; 70import com.android.settings.SettingsPreferenceFragment; 71import com.android.settings.wifi.p2p.WifiP2pSettings; 72 73import java.util.ArrayList; 74import java.util.Collection; 75import java.util.Collections; 76import java.util.HashMap; 77import java.util.List; 78import java.util.concurrent.atomic.AtomicBoolean; 79 80/** 81 * Two types of UI are provided here. 82 * 83 * The first is for "usual Settings", appearing as any other Setup fragment. 84 * 85 * The second is for Setup Wizard, with a simplified interface that hides the action bar 86 * and menus. 87 */ 88public class WifiSettings extends SettingsPreferenceFragment 89 implements DialogInterface.OnClickListener { 90 private static final String TAG = "WifiSettings"; 91 private static final int MENU_ID_WPS_PBC = Menu.FIRST; 92 private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; 93 private static final int MENU_ID_P2P = Menu.FIRST + 2; 94 private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3; 95 private static final int MENU_ID_ADVANCED = Menu.FIRST + 4; 96 private static final int MENU_ID_SCAN = Menu.FIRST + 5; 97 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 98 private static final int MENU_ID_FORGET = Menu.FIRST + 7; 99 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 100 101 private static final int WIFI_DIALOG_ID = 1; 102 private static final int WPS_PBC_DIALOG_ID = 2; 103 private static final int WPS_PIN_DIALOG_ID = 3; 104 105 // Combo scans can take 5-6s to complete - set to 10s. 106 private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; 107 108 // Instance state keys 109 private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode"; 110 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 111 112 private final IntentFilter mFilter; 113 private final BroadcastReceiver mReceiver; 114 private final Scanner mScanner; 115 116 private WifiManager mWifiManager; 117 private WifiManager.Channel mChannel; 118 private WifiManager.ActionListener mConnectListener; 119 private WifiManager.ActionListener mSaveListener; 120 private WifiManager.ActionListener mForgetListener; 121 private boolean mP2pSupported; 122 123 124 private WifiEnabler mWifiEnabler; 125 // An access point being editted is stored here. 126 private AccessPoint mSelectedAccessPoint; 127 128 private DetailedState mLastState; 129 private WifiInfo mLastInfo; 130 131 private AtomicBoolean mConnected = new AtomicBoolean(false); 132 133 private int mKeyStoreNetworkId = INVALID_NETWORK_ID; 134 135 private WifiDialog mDialog; 136 137 private TextView mEmptyView; 138 139 /* Used in Wifi Setup context */ 140 141 // this boolean extra specifies whether to disable the Next button when not connected 142 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 143 144 // this boolean extra specifies whether to auto finish when connection is established 145 private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect"; 146 147 // this boolean extra is set if we are being invoked by the Setup Wizard 148 private static final String EXTRA_IS_FIRST_RUN = "firstRun"; 149 150 // should Next button only be enabled when we have a connection? 151 private boolean mEnableNextOnConnection; 152 153 // should activity finish once we have a connection? 154 private boolean mAutoFinishOnConnection; 155 156 // Save the dialog details 157 private boolean mDlgEdit; 158 private AccessPoint mDlgAccessPoint; 159 private Bundle mAccessPointSavedState; 160 161 // the action bar uses a different set of controls for Setup Wizard 162 private boolean mSetupWizardMode; 163 164 /* End of "used in Wifi Setup context" */ 165 166 public WifiSettings() { 167 mFilter = new IntentFilter(); 168 mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 169 mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 170 mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 171 mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 172 mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 173 mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 174 mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 175 mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 176 177 mReceiver = new BroadcastReceiver() { 178 @Override 179 public void onReceive(Context context, Intent intent) { 180 handleEvent(context, intent); 181 } 182 }; 183 184 mScanner = new Scanner(); 185 } 186 187 @Override 188 public void onCreate(Bundle icicle) { 189 // Set this flag early, as it's needed by getHelpResource(), which is called by super 190 mSetupWizardMode = getActivity().getIntent().getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 191 192 super.onCreate(icicle); 193 } 194 195 @Override 196 public View onCreateView(final LayoutInflater inflater, ViewGroup container, 197 Bundle savedInstanceState) { 198 if (mSetupWizardMode) { 199 View view = inflater.inflate(R.layout.setup_preference, container, false); 200 View other = view.findViewById(R.id.other_network); 201 other.setOnClickListener(new OnClickListener() { 202 @Override 203 public void onClick(View v) { 204 if (mWifiManager.isWifiEnabled()) { 205 onAddNetworkPressed(); 206 } 207 } 208 }); 209 final ImageButton b = (ImageButton) view.findViewById(R.id.more); 210 if (b != null) { 211 b.setOnClickListener(new OnClickListener() { 212 @Override 213 public void onClick(View v) { 214 if (mWifiManager.isWifiEnabled()) { 215 PopupMenu pm = new PopupMenu(inflater.getContext(), b); 216 pm.inflate(R.menu.wifi_setup); 217 pm.setOnMenuItemClickListener(new OnMenuItemClickListener() { 218 @Override 219 public boolean onMenuItemClick(MenuItem item) { 220 if (R.id.wifi_wps == item.getItemId()) { 221 showDialog(WPS_PBC_DIALOG_ID); 222 return true; 223 } 224 return false; 225 } 226 }); 227 pm.show(); 228 } 229 } 230 }); 231 } 232 return view; 233 } else { 234 return super.onCreateView(inflater, container, savedInstanceState); 235 } 236 } 237 238 @Override 239 public void onActivityCreated(Bundle savedInstanceState) { 240 // We don't call super.onActivityCreated() here, since it assumes we already set up 241 // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in 242 // this method. 243 244 mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT); 245 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 246 mChannel = mWifiManager.initialize(getActivity(), getActivity().getMainLooper(), null); 247 248 mConnectListener = new WifiManager.ActionListener() { 249 public void onSuccess() { 250 } 251 public void onFailure(int reason) { 252 Toast.makeText(getActivity(), 253 R.string.wifi_failed_connect_message, 254 Toast.LENGTH_SHORT).show(); 255 } 256 }; 257 258 mSaveListener = new WifiManager.ActionListener() { 259 public void onSuccess() { 260 } 261 public void onFailure(int reason) { 262 Toast.makeText(getActivity(), 263 R.string.wifi_failed_save_message, 264 Toast.LENGTH_SHORT).show(); 265 } 266 }; 267 268 mForgetListener = new WifiManager.ActionListener() { 269 public void onSuccess() { 270 } 271 public void onFailure(int reason) { 272 Toast.makeText(getActivity(), 273 R.string.wifi_failed_forget_message, 274 Toast.LENGTH_SHORT).show(); 275 } 276 }; 277 278 if (savedInstanceState != null 279 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 280 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); 281 mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 282 } 283 284 final Activity activity = getActivity(); 285 final Intent intent = activity.getIntent(); 286 287 // first if we're supposed to finish once we have a connection 288 mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false); 289 290 if (mAutoFinishOnConnection) { 291 // Hide the next button 292 if (hasNextButton()) { 293 getNextButton().setVisibility(View.GONE); 294 } 295 296 final ConnectivityManager connectivity = (ConnectivityManager) 297 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 298 if (connectivity != null 299 && connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) { 300 activity.finish(); 301 return; 302 } 303 } 304 305 // if we're supposed to enable/disable the Next button based on our current connection 306 // state, start it off in the right state 307 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 308 309 if (mEnableNextOnConnection) { 310 if (hasNextButton()) { 311 final ConnectivityManager connectivity = (ConnectivityManager) 312 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 313 if (connectivity != null) { 314 NetworkInfo info = connectivity.getNetworkInfo( 315 ConnectivityManager.TYPE_WIFI); 316 changeNextButtonState(info.isConnected()); 317 } 318 } 319 } 320 321 addPreferencesFromResource(R.xml.wifi_settings); 322 323 if (mSetupWizardMode) { 324 getView().setSystemUiVisibility( 325 View.STATUS_BAR_DISABLE_BACK | 326 View.STATUS_BAR_DISABLE_HOME | 327 View.STATUS_BAR_DISABLE_RECENT | 328 View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS | 329 View.STATUS_BAR_DISABLE_CLOCK); 330 } 331 332 // On/off switch is hidden for Setup Wizard 333 if (!mSetupWizardMode) { 334 Switch actionBarSwitch = new Switch(activity); 335 336 if (activity instanceof PreferenceActivity) { 337 PreferenceActivity preferenceActivity = (PreferenceActivity) activity; 338 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { 339 final int padding = activity.getResources().getDimensionPixelSize( 340 R.dimen.action_bar_switch_padding); 341 actionBarSwitch.setPadding(0, 0, padding, 0); 342 activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 343 ActionBar.DISPLAY_SHOW_CUSTOM); 344 activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( 345 ActionBar.LayoutParams.WRAP_CONTENT, 346 ActionBar.LayoutParams.WRAP_CONTENT, 347 Gravity.CENTER_VERTICAL | Gravity.RIGHT)); 348 } 349 } 350 351 mWifiEnabler = new WifiEnabler(activity, actionBarSwitch); 352 } 353 354 mEmptyView = (TextView) getView().findViewById(android.R.id.empty); 355 getListView().setEmptyView(mEmptyView); 356 357 if (!mSetupWizardMode) { 358 registerForContextMenu(getListView()); 359 } 360 setHasOptionsMenu(true); 361 362 // After confirming PreferenceScreen is available, we call super. 363 super.onActivityCreated(savedInstanceState); 364 } 365 366 @Override 367 public void onResume() { 368 super.onResume(); 369 if (mWifiEnabler != null) { 370 mWifiEnabler.resume(); 371 } 372 373 getActivity().registerReceiver(mReceiver, mFilter); 374 if (mKeyStoreNetworkId != INVALID_NETWORK_ID && 375 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) { 376 mWifiManager.connect(mChannel, mKeyStoreNetworkId, mConnectListener); 377 } 378 mKeyStoreNetworkId = INVALID_NETWORK_ID; 379 380 updateAccessPoints(); 381 } 382 383 @Override 384 public void onPause() { 385 super.onPause(); 386 if (mWifiEnabler != null) { 387 mWifiEnabler.pause(); 388 } 389 getActivity().unregisterReceiver(mReceiver); 390 mScanner.pause(); 391 } 392 393 @Override 394 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 395 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 396 if (mSetupWizardMode) { 397 // FIXME: add setIcon() when graphics are available 398 menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc) 399 .setIcon(R.drawable.ic_wps) 400 .setEnabled(wifiIsEnabled) 401 .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 402 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 403 .setEnabled(wifiIsEnabled) 404 .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 405 } else { 406 menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc) 407 .setIcon(R.drawable.ic_wps) 408 .setEnabled(wifiIsEnabled) 409 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 410 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 411 .setIcon(R.drawable.ic_menu_add) 412 .setEnabled(wifiIsEnabled) 413 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 414 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) 415 //.setIcon(R.drawable.ic_menu_scan_network) 416 .setEnabled(wifiIsEnabled) 417 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 418 menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin) 419 .setEnabled(wifiIsEnabled) 420 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 421 if (mP2pSupported) { 422 menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p) 423 .setEnabled(wifiIsEnabled) 424 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 425 } 426 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 427 //.setIcon(android.R.drawable.ic_menu_manage) 428 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 429 } 430 super.onCreateOptionsMenu(menu, inflater); 431 } 432 433 @Override 434 public void onSaveInstanceState(Bundle outState) { 435 super.onSaveInstanceState(outState); 436 437 // If the dialog is showing, save its state. 438 if (mDialog != null && mDialog.isShowing()) { 439 outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit); 440 if (mDlgAccessPoint != null) { 441 mAccessPointSavedState = new Bundle(); 442 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 443 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 444 } 445 } 446 } 447 448 @Override 449 public boolean onOptionsItemSelected(MenuItem item) { 450 switch (item.getItemId()) { 451 case MENU_ID_WPS_PBC: 452 showDialog(WPS_PBC_DIALOG_ID); 453 return true; 454 case MENU_ID_P2P: 455 if (getActivity() instanceof PreferenceActivity) { 456 ((PreferenceActivity) getActivity()).startPreferencePanel( 457 WifiP2pSettings.class.getCanonicalName(), 458 null, 459 R.string.wifi_p2p_settings_title, null, 460 this, 0); 461 } else { 462 startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null); 463 } 464 return true; 465 case MENU_ID_WPS_PIN: 466 showDialog(WPS_PIN_DIALOG_ID); 467 return true; 468 case MENU_ID_SCAN: 469 if (mWifiManager.isWifiEnabled()) { 470 mScanner.forceScan(); 471 } 472 return true; 473 case MENU_ID_ADD_NETWORK: 474 if (mWifiManager.isWifiEnabled()) { 475 onAddNetworkPressed(); 476 } 477 return true; 478 case MENU_ID_ADVANCED: 479 if (getActivity() instanceof PreferenceActivity) { 480 ((PreferenceActivity) getActivity()).startPreferencePanel( 481 AdvancedWifiSettings.class.getCanonicalName(), 482 null, 483 R.string.wifi_advanced_titlebar, null, 484 this, 0); 485 } else { 486 startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null); 487 } 488 return true; 489 } 490 return super.onOptionsItemSelected(item); 491 } 492 493 @Override 494 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 495 if (info instanceof AdapterContextMenuInfo) { 496 Preference preference = (Preference) getListView().getItemAtPosition( 497 ((AdapterContextMenuInfo) info).position); 498 499 if (preference instanceof AccessPoint) { 500 mSelectedAccessPoint = (AccessPoint) preference; 501 menu.setHeaderTitle(mSelectedAccessPoint.ssid); 502 if (mSelectedAccessPoint.getLevel() != -1 503 && mSelectedAccessPoint.getState() == null) { 504 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 505 } 506 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 507 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 508 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 509 } 510 } 511 } 512 } 513 514 @Override 515 public boolean onContextItemSelected(MenuItem item) { 516 if (mSelectedAccessPoint == null) { 517 return super.onContextItemSelected(item); 518 } 519 switch (item.getItemId()) { 520 case MENU_ID_CONNECT: { 521 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 522 if (!requireKeyStore(mSelectedAccessPoint.getConfig())) { 523 mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId, 524 mConnectListener); 525 } 526 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { 527 /** Bypass dialog for unsecured networks */ 528 mSelectedAccessPoint.generateOpenNetworkConfig(); 529 mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(), 530 mConnectListener); 531 } else { 532 showDialog(mSelectedAccessPoint, true); 533 } 534 return true; 535 } 536 case MENU_ID_FORGET: { 537 mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener); 538 return true; 539 } 540 case MENU_ID_MODIFY: { 541 showDialog(mSelectedAccessPoint, true); 542 return true; 543 } 544 } 545 return super.onContextItemSelected(item); 546 } 547 548 @Override 549 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 550 if (preference instanceof AccessPoint) { 551 mSelectedAccessPoint = (AccessPoint) preference; 552 /** Bypass dialog for unsecured, unsaved networks */ 553 if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE && 554 mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 555 mSelectedAccessPoint.generateOpenNetworkConfig(); 556 mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(), mConnectListener); 557 } else { 558 showDialog(mSelectedAccessPoint, false); 559 } 560 } else { 561 return super.onPreferenceTreeClick(screen, preference); 562 } 563 return true; 564 } 565 566 private void showDialog(AccessPoint accessPoint, boolean edit) { 567 if (mDialog != null) { 568 removeDialog(WIFI_DIALOG_ID); 569 mDialog = null; 570 } 571 572 // Save the access point and edit mode 573 mDlgAccessPoint = accessPoint; 574 mDlgEdit = edit; 575 576 showDialog(WIFI_DIALOG_ID); 577 } 578 579 @Override 580 public Dialog onCreateDialog(int dialogId) { 581 switch (dialogId) { 582 case WIFI_DIALOG_ID: 583 AccessPoint ap = mDlgAccessPoint; // For manual launch 584 if (ap == null) { // For re-launch from saved state 585 if (mAccessPointSavedState != null) { 586 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 587 // For repeated orientation changes 588 mDlgAccessPoint = ap; 589 } 590 } 591 // If it's still null, fine, it's for Add Network 592 mSelectedAccessPoint = ap; 593 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit); 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 } 600 return super.onCreateDialog(dialogId); 601 } 602 603 private boolean requireKeyStore(WifiConfiguration config) { 604 if (WifiConfigController.requireKeyStore(config) && 605 KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) { 606 mKeyStoreNetworkId = config.networkId; 607 Credentials.getInstance().unlock(getActivity()); 608 return true; 609 } 610 return false; 611 } 612 613 /** 614 * Shows the latest access points available with supplimental information like 615 * the strength of network and the security for it. 616 */ 617 private void updateAccessPoints() { 618 // Safeguard from some delayed event handling 619 if (getActivity() == null) return; 620 621 final int wifiState = mWifiManager.getWifiState(); 622 623 switch (wifiState) { 624 case WifiManager.WIFI_STATE_ENABLED: 625 // AccessPoints are automatically sorted with TreeSet. 626 final Collection<AccessPoint> accessPoints = constructAccessPoints(); 627 getPreferenceScreen().removeAll(); 628 if(accessPoints.size() == 0) { 629 addMessagePreference(R.string.wifi_empty_list_wifi_on); 630 } 631 for (AccessPoint accessPoint : accessPoints) { 632 getPreferenceScreen().addPreference(accessPoint); 633 } 634 break; 635 636 case WifiManager.WIFI_STATE_ENABLING: 637 getPreferenceScreen().removeAll(); 638 break; 639 640 case WifiManager.WIFI_STATE_DISABLING: 641 addMessagePreference(R.string.wifi_stopping); 642 break; 643 644 case WifiManager.WIFI_STATE_DISABLED: 645 addMessagePreference(R.string.wifi_empty_list_wifi_off); 646 break; 647 } 648 } 649 650 private void addMessagePreference(int messageId) { 651 if (mEmptyView != null) mEmptyView.setText(messageId); 652 getPreferenceScreen().removeAll(); 653 } 654 655 /** Returns sorted list of access points */ 656 private List<AccessPoint> constructAccessPoints() { 657 ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 658 /** Lookup table to more quickly update AccessPoints by only considering objects with the 659 * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ 660 Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); 661 662 final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 663 if (configs != null) { 664 for (WifiConfiguration config : configs) { 665 AccessPoint accessPoint = new AccessPoint(getActivity(), config); 666 accessPoint.update(mLastInfo, mLastState); 667 accessPoints.add(accessPoint); 668 apMap.put(accessPoint.ssid, accessPoint); 669 } 670 } 671 672 final List<ScanResult> results = mWifiManager.getScanResults(); 673 if (results != null) { 674 for (ScanResult result : results) { 675 // Ignore hidden and ad-hoc networks. 676 if (result.SSID == null || result.SSID.length() == 0 || 677 result.capabilities.contains("[IBSS]")) { 678 continue; 679 } 680 681 boolean found = false; 682 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { 683 if (accessPoint.update(result)) 684 found = true; 685 } 686 if (!found) { 687 AccessPoint accessPoint = new AccessPoint(getActivity(), result); 688 accessPoints.add(accessPoint); 689 apMap.put(accessPoint.ssid, accessPoint); 690 } 691 } 692 } 693 694 // Pre-sort accessPoints to speed preference insertion 695 Collections.sort(accessPoints); 696 return accessPoints; 697 } 698 699 /** A restricted multimap for use in constructAccessPoints */ 700 private class Multimap<K,V> { 701 private HashMap<K,List<V>> store = new HashMap<K,List<V>>(); 702 /** retrieve a non-null list of values with key K */ 703 List<V> getAll(K key) { 704 List<V> values = store.get(key); 705 return values != null ? values : Collections.<V>emptyList(); 706 } 707 708 void put(K key, V val) { 709 List<V> curVals = store.get(key); 710 if (curVals == null) { 711 curVals = new ArrayList<V>(3); 712 store.put(key, curVals); 713 } 714 curVals.add(val); 715 } 716 } 717 718 private void handleEvent(Context context, Intent intent) { 719 String action = intent.getAction(); 720 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 721 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 722 WifiManager.WIFI_STATE_UNKNOWN)); 723 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || 724 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || 725 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { 726 updateAccessPoints(); 727 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 728 //Ignore supplicant state changes when network is connected 729 //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and 730 //introduce a broadcast that combines the supplicant and network 731 //network state change events so the apps dont have to worry about 732 //ignoring supplicant state change when network is connected 733 //to get more fine grained information. 734 SupplicantState state = (SupplicantState) intent.getParcelableExtra( 735 WifiManager.EXTRA_NEW_STATE); 736 if (!mConnected.get() && SupplicantState.isHandshakeState(state)) { 737 updateConnectionState(WifiInfo.getDetailedStateOf(state)); 738 } 739 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 740 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 741 WifiManager.EXTRA_NETWORK_INFO); 742 mConnected.set(info.isConnected()); 743 changeNextButtonState(info.isConnected()); 744 updateAccessPoints(); 745 updateConnectionState(info.getDetailedState()); 746 if (mAutoFinishOnConnection && info.isConnected()) { 747 getActivity().finish(); 748 return; 749 } 750 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 751 updateConnectionState(null); 752 } 753 } 754 755 private void updateConnectionState(DetailedState state) { 756 /* sticky broadcasts can call this when wifi is disabled */ 757 if (!mWifiManager.isWifiEnabled()) { 758 mScanner.pause(); 759 return; 760 } 761 762 if (state == DetailedState.OBTAINING_IPADDR) { 763 mScanner.pause(); 764 } else { 765 mScanner.resume(); 766 } 767 768 mLastInfo = mWifiManager.getConnectionInfo(); 769 if (state != null) { 770 mLastState = state; 771 } 772 773 for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) { 774 // Maybe there's a WifiConfigPreference 775 Preference preference = getPreferenceScreen().getPreference(i); 776 if (preference instanceof AccessPoint) { 777 final AccessPoint accessPoint = (AccessPoint) preference; 778 accessPoint.update(mLastInfo, mLastState); 779 } 780 } 781 } 782 783 private void updateWifiState(int state) { 784 getActivity().invalidateOptionsMenu(); 785 786 switch (state) { 787 case WifiManager.WIFI_STATE_ENABLED: 788 mScanner.resume(); 789 return; // not break, to avoid the call to pause() below 790 791 case WifiManager.WIFI_STATE_ENABLING: 792 addMessagePreference(R.string.wifi_starting); 793 break; 794 795 case WifiManager.WIFI_STATE_DISABLED: 796 addMessagePreference(R.string.wifi_empty_list_wifi_off); 797 break; 798 } 799 800 mLastInfo = null; 801 mLastState = null; 802 mScanner.pause(); 803 } 804 805 private class Scanner extends Handler { 806 private int mRetry = 0; 807 808 void resume() { 809 if (!hasMessages(0)) { 810 sendEmptyMessage(0); 811 } 812 } 813 814 void forceScan() { 815 removeMessages(0); 816 sendEmptyMessage(0); 817 } 818 819 void pause() { 820 mRetry = 0; 821 removeMessages(0); 822 } 823 824 @Override 825 public void handleMessage(Message message) { 826 if (mWifiManager.startScanActive()) { 827 mRetry = 0; 828 } else if (++mRetry >= 3) { 829 mRetry = 0; 830 Toast.makeText(getActivity(), R.string.wifi_fail_to_scan, 831 Toast.LENGTH_LONG).show(); 832 return; 833 } 834 sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); 835 } 836 } 837 838 /** 839 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 840 * Wifi setup screens, not in usual wifi settings screen. 841 * 842 * @param connected true when the device is connected to a wifi network. 843 */ 844 private void changeNextButtonState(boolean connected) { 845 if (mEnableNextOnConnection && hasNextButton()) { 846 getNextButton().setEnabled(connected); 847 } 848 } 849 850 public void onClick(DialogInterface dialogInterface, int button) { 851 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 852 forget(); 853 } else if (button == WifiDialog.BUTTON_SUBMIT) { 854 submit(mDialog.getController()); 855 } 856 } 857 858 /* package */ void submit(WifiConfigController configController) { 859 860 final WifiConfiguration config = configController.getConfig(); 861 862 if (config == null) { 863 if (mSelectedAccessPoint != null 864 && !requireKeyStore(mSelectedAccessPoint.getConfig()) 865 && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 866 mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId, 867 mConnectListener); 868 } 869 } else if (config.networkId != INVALID_NETWORK_ID) { 870 if (mSelectedAccessPoint != null) { 871 mWifiManager.save(mChannel, config, mSaveListener); 872 } 873 } else { 874 if (configController.isEdit() || requireKeyStore(config)) { 875 mWifiManager.save(mChannel, config, mSaveListener); 876 } else { 877 mWifiManager.connect(mChannel, config, mConnectListener); 878 } 879 } 880 881 if (mWifiManager.isWifiEnabled()) { 882 mScanner.resume(); 883 } 884 updateAccessPoints(); 885 } 886 887 /* package */ void forget() { 888 if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 889 // Should not happen, but a monkey seems to triger it 890 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 891 return; 892 } 893 894 mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener); 895 896 if (mWifiManager.isWifiEnabled()) { 897 mScanner.resume(); 898 } 899 updateAccessPoints(); 900 901 // We need to rename/replace "Next" button in wifi setup context. 902 changeNextButtonState(false); 903 } 904 905 /** 906 * Refreshes acccess points and ask Wifi module to scan networks again. 907 */ 908 /* package */ void refreshAccessPoints() { 909 if (mWifiManager.isWifiEnabled()) { 910 mScanner.resume(); 911 } 912 913 getPreferenceScreen().removeAll(); 914 } 915 916 /** 917 * Called when "add network" button is pressed. 918 */ 919 /* package */ void onAddNetworkPressed() { 920 // No exact access point is selected. 921 mSelectedAccessPoint = null; 922 showDialog(null, true); 923 } 924 925 /* package */ int getAccessPointsCount() { 926 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 927 if (wifiIsEnabled) { 928 return getPreferenceScreen().getPreferenceCount(); 929 } else { 930 return 0; 931 } 932 } 933 934 /** 935 * Requests wifi module to pause wifi scan. May be ignored when the module is disabled. 936 */ 937 /* package */ void pauseWifiScan() { 938 if (mWifiManager.isWifiEnabled()) { 939 mScanner.pause(); 940 } 941 } 942 943 /** 944 * Requests wifi module to resume wifi scan. May be ignored when the module is disabled. 945 */ 946 /* package */ void resumeWifiScan() { 947 if (mWifiManager.isWifiEnabled()) { 948 mScanner.resume(); 949 } 950 } 951 952 @Override 953 protected int getHelpResource() { 954 if (mSetupWizardMode) { 955 return 0; 956 } 957 return R.string.help_url_wifi; 958 } 959 960 /** 961 * Used as the outer frame of all setup wizard pages that need to adjust their margins based 962 * on the total size of the available display. (e.g. side margins set to 10% of total width.) 963 */ 964 public static class ProportionalOuterFrame extends RelativeLayout { 965 public ProportionalOuterFrame(Context context) { 966 super(context); 967 } 968 public ProportionalOuterFrame(Context context, AttributeSet attrs) { 969 super(context, attrs); 970 } 971 public ProportionalOuterFrame(Context context, AttributeSet attrs, int defStyle) { 972 super(context, attrs, defStyle); 973 } 974 975 /** 976 * Set our margins and title area height proportionally to the available display size 977 */ 978 @Override 979 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 980 int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 981 int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 982 final Resources resources = getContext().getResources(); 983 float titleHeight = resources.getFraction(R.dimen.setup_title_height, 1, 1); 984 float sideMargin = resources.getFraction(R.dimen.setup_border_width, 1, 1); 985 int bottom = resources.getDimensionPixelSize(R.dimen.setup_margin_bottom); 986 setPadding( 987 (int) (parentWidth * sideMargin), 988 0, 989 (int) (parentWidth * sideMargin), 990 bottom); 991 View title = findViewById(R.id.title_area); 992 if (title != null) { 993 title.setMinimumHeight((int) (parentHeight * titleHeight)); 994 } 995 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 996 } 997 } 998 999} 1000