WifiSettings.java revision fdac5bae251bd0b569c38aa8575b89ba0895911c
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.AlertDialog; 24import android.app.Dialog; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.net.ConnectivityManager; 31import android.net.NetworkInfo; 32import android.net.NetworkInfo.DetailedState; 33import android.net.wifi.ScanResult; 34import android.net.wifi.SupplicantState; 35import android.net.wifi.WifiConfiguration; 36import android.net.wifi.WifiConfiguration.KeyMgmt; 37import android.net.wifi.WifiInfo; 38import android.net.wifi.WifiManager; 39import android.net.wifi.WpsResult; 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.Log; 49import android.view.ContextMenu; 50import android.view.ContextMenu.ContextMenuInfo; 51import android.view.Gravity; 52import android.view.Menu; 53import android.view.MenuInflater; 54import android.view.MenuItem; 55import android.view.View; 56import android.widget.AdapterView.AdapterContextMenuInfo; 57import android.widget.Switch; 58import android.widget.TextView; 59import android.widget.Toast; 60 61import com.android.internal.util.AsyncChannel; 62import com.android.settings.R; 63import com.android.settings.SettingsPreferenceFragment; 64 65import java.util.ArrayList; 66import java.util.Collection; 67import java.util.Collections; 68import java.util.HashMap; 69import java.util.List; 70import java.util.concurrent.atomic.AtomicBoolean; 71 72/** 73 * This currently provides three types of UI. 74 * 75 * Two are for phones with relatively small screens: "for SetupWizard" and "for usual Settings". 76 * Users just need to launch WifiSettings Activity as usual. The request will be appropriately 77 * handled by ActivityManager, and they will have appropriate look-and-feel with this fragment. 78 * 79 * Third type is for Setup Wizard with X-Large, landscape UI. Users need to launch 80 * {@link WifiSettingsForSetupWizardXL} Activity, which contains this fragment but also has 81 * other decorations specific to that screen. 82 */ 83public class WifiSettings extends SettingsPreferenceFragment 84 implements DialogInterface.OnClickListener { 85 private static final String TAG = "WifiSettings"; 86 private static final int MENU_ID_SCAN = Menu.FIRST; 87 private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 1; 88 private static final int MENU_ID_ADVANCED = Menu.FIRST + 2; 89 private static final int MENU_ID_CONNECT = Menu.FIRST + 3; 90 private static final int MENU_ID_FORGET = Menu.FIRST + 4; 91 private static final int MENU_ID_MODIFY = Menu.FIRST + 5; 92 93 private static final int WIFI_DIALOG_ID = 1; 94 95 // Combo scans can take 5-6s to complete - set to 10s. 96 private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; 97 98 // Instance state keys 99 private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode"; 100 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 101 102 private final IntentFilter mFilter; 103 private final BroadcastReceiver mReceiver; 104 private final Scanner mScanner; 105 106 private WifiManager mWifiManager; 107 private WifiEnabler mWifiEnabler; 108 // An access point being editted is stored here. 109 private AccessPoint mSelectedAccessPoint; 110 111 private DetailedState mLastState; 112 private WifiInfo mLastInfo; 113 114 private AtomicBoolean mConnected = new AtomicBoolean(false); 115 116 private int mKeyStoreNetworkId = INVALID_NETWORK_ID; 117 118 private WifiDialog mDialog; 119 120 private TextView mEmptyView; 121 122 /* Used in Wifi Setup context */ 123 124 // this boolean extra specifies whether to disable the Next button when not connected 125 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 126 127 // should Next button only be enabled when we have a connection? 128 private boolean mEnableNextOnConnection; 129 private boolean mInXlSetupWizard; 130 131 // Save the dialog details 132 private boolean mDlgEdit; 133 private AccessPoint mDlgAccessPoint; 134 private Bundle mAccessPointSavedState; 135 136 /* End of "used in Wifi Setup context" */ 137 138 public WifiSettings() { 139 mFilter = new IntentFilter(); 140 mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 141 mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 142 mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 143 mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 144 mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 145 mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 146 mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 147 mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 148 mFilter.addAction(WifiManager.ERROR_ACTION); 149 150 mReceiver = new BroadcastReceiver() { 151 @Override 152 public void onReceive(Context context, Intent intent) { 153 handleEvent(context, intent); 154 } 155 }; 156 157 mScanner = new Scanner(); 158 } 159 160 @Override 161 public void onAttach(Activity activity) { 162 super.onAttach(activity); 163 164 mInXlSetupWizard = (activity instanceof WifiSettingsForSetupWizardXL); 165 } 166 167 @Override 168 public void onActivityCreated(Bundle savedInstanceState) { 169 // We don't call super.onActivityCreated() here, since it assumes we already set up 170 // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in 171 // this method. 172 173 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 174 mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler()); 175 if (savedInstanceState != null 176 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 177 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); 178 mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 179 } 180 181 final Activity activity = getActivity(); 182 final Intent intent = activity.getIntent(); 183 184 // if we're supposed to enable/disable the Next button based on our current connection 185 // state, start it off in the right state 186 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 187 188 if (mEnableNextOnConnection) { 189 if (hasNextButton()) { 190 final ConnectivityManager connectivity = (ConnectivityManager) 191 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 192 if (connectivity != null) { 193 NetworkInfo info = connectivity.getNetworkInfo( 194 ConnectivityManager.TYPE_WIFI); 195 changeNextButtonState(info.isConnected()); 196 } 197 } 198 } 199 200 if (mInXlSetupWizard) { 201 addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl); 202 } else { 203 addPreferencesFromResource(R.xml.wifi_settings); 204 205 Switch actionBarSwitch = new Switch(activity); 206 207 if (activity instanceof PreferenceActivity) { 208 PreferenceActivity preferenceActivity = (PreferenceActivity) activity; 209 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { 210 final int padding = activity.getResources().getDimensionPixelSize( 211 R.dimen.action_bar_switch_padding); 212 actionBarSwitch.setPadding(0, 0, padding, 0); 213 activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 214 ActionBar.DISPLAY_SHOW_CUSTOM); 215 activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( 216 ActionBar.LayoutParams.WRAP_CONTENT, 217 ActionBar.LayoutParams.WRAP_CONTENT, 218 Gravity.CENTER_VERTICAL | Gravity.RIGHT)); 219 } 220 } 221 222 mWifiEnabler = new WifiEnabler(activity, actionBarSwitch); 223 } 224 225 mEmptyView = (TextView) getView().findViewById(android.R.id.empty); 226 getListView().setEmptyView(mEmptyView); 227 228 registerForContextMenu(getListView()); 229 setHasOptionsMenu(true); 230 231 // After confirming PreferenceScreen is available, we call super. 232 super.onActivityCreated(savedInstanceState); 233 } 234 235 @Override 236 public void onResume() { 237 super.onResume(); 238 if (mWifiEnabler != null) { 239 mWifiEnabler.resume(); 240 } 241 242 getActivity().registerReceiver(mReceiver, mFilter); 243 if (mKeyStoreNetworkId != INVALID_NETWORK_ID && 244 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) { 245 mWifiManager.connectNetwork(mKeyStoreNetworkId); 246 } 247 mKeyStoreNetworkId = INVALID_NETWORK_ID; 248 249 updateAccessPoints(); 250 } 251 252 @Override 253 public void onPause() { 254 super.onPause(); 255 if (mWifiEnabler != null) { 256 mWifiEnabler.pause(); 257 } 258 getActivity().unregisterReceiver(mReceiver); 259 mScanner.pause(); 260 } 261 262 @Override 263 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 264 // We don't want menus in Setup Wizard XL. 265 if (!mInXlSetupWizard) { 266 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 267 menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) 268 //.setIcon(R.drawable.ic_menu_scan_network) 269 .setEnabled(wifiIsEnabled) 270 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 271 menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) 272 .setEnabled(wifiIsEnabled) 273 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 274 menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 275 //.setIcon(android.R.drawable.ic_menu_manage) 276 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 277 } 278 super.onCreateOptionsMenu(menu, inflater); 279 } 280 281 @Override 282 public void onSaveInstanceState(Bundle outState) { 283 super.onSaveInstanceState(outState); 284 285 // If the dialog is showing, save its state. 286 if (mDialog != null && mDialog.isShowing()) { 287 outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit); 288 if (mDlgAccessPoint != null) { 289 mAccessPointSavedState = new Bundle(); 290 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 291 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 292 } 293 } 294 } 295 296 @Override 297 public boolean onOptionsItemSelected(MenuItem item) { 298 switch (item.getItemId()) { 299 case MENU_ID_SCAN: 300 if (mWifiManager.isWifiEnabled()) { 301 mScanner.forceScan(); 302 } 303 return true; 304 case MENU_ID_ADD_NETWORK: 305 if (mWifiManager.isWifiEnabled()) { 306 onAddNetworkPressed(); 307 } 308 return true; 309 case MENU_ID_ADVANCED: 310 if (getActivity() instanceof PreferenceActivity) { 311 ((PreferenceActivity) getActivity()).startPreferencePanel( 312 AdvancedWifiSettings.class.getCanonicalName(), 313 null, 314 R.string.wifi_advanced_titlebar, null, 315 this, 0); 316 } else { 317 startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null); 318 } 319 return true; 320 } 321 return super.onOptionsItemSelected(item); 322 } 323 324 @Override 325 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 326 if (mInXlSetupWizard) { 327 ((WifiSettingsForSetupWizardXL)getActivity()).onCreateContextMenu(menu, view, info); 328 } else if (info instanceof AdapterContextMenuInfo) { 329 Preference preference = (Preference) getListView().getItemAtPosition( 330 ((AdapterContextMenuInfo) info).position); 331 332 if (preference instanceof AccessPoint) { 333 mSelectedAccessPoint = (AccessPoint) preference; 334 menu.setHeaderTitle(mSelectedAccessPoint.ssid); 335 if (mSelectedAccessPoint.getLevel() != -1 336 && mSelectedAccessPoint.getState() == null) { 337 menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); 338 } 339 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 340 menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); 341 menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); 342 } 343 } 344 } 345 } 346 347 @Override 348 public boolean onContextItemSelected(MenuItem item) { 349 if (mSelectedAccessPoint == null) { 350 return super.onContextItemSelected(item); 351 } 352 switch (item.getItemId()) { 353 case MENU_ID_CONNECT: { 354 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 355 if (!requireKeyStore(mSelectedAccessPoint.getConfig())) { 356 mWifiManager.connectNetwork(mSelectedAccessPoint.networkId); 357 } 358 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { 359 /** Bypass dialog for unsecured networks */ 360 mSelectedAccessPoint.generateOpenNetworkConfig(); 361 mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig()); 362 } else { 363 showConfigUi(mSelectedAccessPoint, true); 364 } 365 return true; 366 } 367 case MENU_ID_FORGET: { 368 mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId); 369 return true; 370 } 371 case MENU_ID_MODIFY: { 372 showConfigUi(mSelectedAccessPoint, true); 373 return true; 374 } 375 } 376 return super.onContextItemSelected(item); 377 } 378 379 @Override 380 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 381 if (preference instanceof AccessPoint) { 382 mSelectedAccessPoint = (AccessPoint) preference; 383 /** Bypass dialog for unsecured, unsaved networks */ 384 if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE && 385 mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) { 386 mSelectedAccessPoint.generateOpenNetworkConfig(); 387 mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig()); 388 } else { 389 showConfigUi(mSelectedAccessPoint, false); 390 } 391 } else { 392 return super.onPreferenceTreeClick(screen, preference); 393 } 394 return true; 395 } 396 397 /** 398 * Shows an appropriate Wifi configuration component. 399 * Called when a user clicks "Add network" preference or one of available networks is selected. 400 */ 401 private void showConfigUi(AccessPoint accessPoint, boolean edit) { 402 if (mInXlSetupWizard) { 403 ((WifiSettingsForSetupWizardXL)getActivity()).showConfigUi(accessPoint, edit); 404 } else { 405 showDialog(accessPoint, edit); 406 } 407 } 408 409 private void showDialog(AccessPoint accessPoint, boolean edit) { 410 if (mDialog != null) { 411 removeDialog(WIFI_DIALOG_ID); 412 mDialog = null; 413 } 414 415 // Save the access point and edit mode 416 mDlgAccessPoint = accessPoint; 417 mDlgEdit = edit; 418 419 showDialog(WIFI_DIALOG_ID); 420 } 421 422 @Override 423 public Dialog onCreateDialog(int dialogId) { 424 AccessPoint ap = mDlgAccessPoint; // For manual launch 425 if (ap == null) { // For re-launch from saved state 426 if (mAccessPointSavedState != null) { 427 ap = new AccessPoint(getActivity(), mAccessPointSavedState); 428 // For repeated orientation changes 429 mDlgAccessPoint = ap; 430 } 431 } 432 // If it's still null, fine, it's for Add Network 433 mSelectedAccessPoint = ap; 434 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit); 435 return mDialog; 436 } 437 438 private boolean requireKeyStore(WifiConfiguration config) { 439 if (WifiConfigController.requireKeyStore(config) && 440 KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) { 441 mKeyStoreNetworkId = config.networkId; 442 Credentials.getInstance().unlock(getActivity()); 443 return true; 444 } 445 return false; 446 } 447 448 /** 449 * Shows the latest access points available with supplimental information like 450 * the strength of network and the security for it. 451 */ 452 private void updateAccessPoints() { 453 final int wifiState = mWifiManager.getWifiState(); 454 455 switch (wifiState) { 456 case WifiManager.WIFI_STATE_ENABLED: 457 // AccessPoints are automatically sorted with TreeSet. 458 final Collection<AccessPoint> accessPoints = constructAccessPoints(); 459 getPreferenceScreen().removeAll(); 460 if (mInXlSetupWizard) { 461 ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated( 462 getPreferenceScreen(), accessPoints); 463 } else { 464 for (AccessPoint accessPoint : accessPoints) { 465 getPreferenceScreen().addPreference(accessPoint); 466 } 467 } 468 break; 469 470 case WifiManager.WIFI_STATE_ENABLING: 471 getPreferenceScreen().removeAll(); 472 break; 473 474 case WifiManager.WIFI_STATE_DISABLING: 475 addMessagePreference(R.string.wifi_stopping); 476 break; 477 478 case WifiManager.WIFI_STATE_DISABLED: 479 addMessagePreference(R.string.wifi_empty_list_wifi_off); 480 break; 481 } 482 } 483 484 private void addMessagePreference(int messageId) { 485 if (mEmptyView != null) mEmptyView.setText(messageId); 486 getPreferenceScreen().removeAll(); 487 } 488 489 /** Returns sorted list of access points */ 490 private List<AccessPoint> constructAccessPoints() { 491 ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); 492 /** Lookup table to more quickly update AccessPoints by only considering objects with the 493 * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ 494 Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); 495 496 final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 497 if (configs != null) { 498 for (WifiConfiguration config : configs) { 499 AccessPoint accessPoint = new AccessPoint(getActivity(), config); 500 accessPoint.update(mLastInfo, mLastState); 501 accessPoints.add(accessPoint); 502 apMap.put(accessPoint.ssid, accessPoint); 503 } 504 } 505 506 final List<ScanResult> results = mWifiManager.getScanResults(); 507 if (results != null) { 508 for (ScanResult result : results) { 509 // Ignore hidden and ad-hoc networks. 510 if (result.SSID == null || result.SSID.length() == 0 || 511 result.capabilities.contains("[IBSS]")) { 512 continue; 513 } 514 515 boolean found = false; 516 if (apMap.getAll(result.SSID) != null) { 517 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { 518 if (accessPoint.update(result)) 519 found = true; 520 } 521 } 522 if (!found) { 523 AccessPoint accessPoint = new AccessPoint(getActivity(), result); 524 accessPoints.add(accessPoint); 525 apMap.put(accessPoint.ssid, accessPoint); 526 } 527 } 528 } 529 530 // 531 Collections.sort(accessPoints); 532 return accessPoints; 533 } 534 535 /** A restricted multimap for use in constructAccessPoints */ 536 private class Multimap<K,V> { 537 private HashMap<K,List<V>> store = new HashMap<K,List<V>>(); 538 /** retrieve a possibly null list of values with key K */ 539 List<V> getAll(K key) { 540 return store.get(key); 541 } 542 543 void put(K key, V val) { 544 List<V> curVals = store.get(key); 545 if (curVals == null) { 546 curVals = new ArrayList<V>(3); 547 store.put(key, curVals); 548 } 549 curVals.add(val); 550 } 551 } 552 553 private void handleEvent(Context context, Intent intent) { 554 String action = intent.getAction(); 555 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 556 updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 557 WifiManager.WIFI_STATE_UNKNOWN)); 558 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || 559 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || 560 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { 561 updateAccessPoints(); 562 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 563 //Ignore supplicant state changes when network is connected 564 //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and 565 //introduce a broadcast that combines the supplicant and network 566 //network state change events so the apps dont have to worry about 567 //ignoring supplicant state change when network is connected 568 //to get more fine grained information. 569 if (!mConnected.get()) { 570 updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState) 571 intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE))); 572 } 573 574 if (mInXlSetupWizard) { 575 ((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent); 576 } 577 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 578 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 579 WifiManager.EXTRA_NETWORK_INFO); 580 mConnected.set(info.isConnected()); 581 changeNextButtonState(info.isConnected()); 582 updateAccessPoints(); 583 updateConnectionState(info.getDetailedState()); 584 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 585 updateConnectionState(null); 586 } else if (WifiManager.ERROR_ACTION.equals(action)) { 587 int errorCode = intent.getIntExtra(WifiManager.EXTRA_ERROR_CODE, 0); 588 switch (errorCode) { 589 case WifiManager.WPS_OVERLAP_ERROR: 590 Toast.makeText(context, R.string.wifi_wps_overlap_error, 591 Toast.LENGTH_SHORT).show(); 592 break; 593 } 594 } 595 } 596 597 private void updateConnectionState(DetailedState state) { 598 /* sticky broadcasts can call this when wifi is disabled */ 599 if (!mWifiManager.isWifiEnabled()) { 600 mScanner.pause(); 601 return; 602 } 603 604 if (state == DetailedState.OBTAINING_IPADDR) { 605 mScanner.pause(); 606 } else { 607 mScanner.resume(); 608 } 609 610 mLastInfo = mWifiManager.getConnectionInfo(); 611 if (state != null) { 612 mLastState = state; 613 } 614 615 for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) { 616 // Maybe there's a WifiConfigPreference 617 Preference preference = getPreferenceScreen().getPreference(i); 618 if (preference instanceof AccessPoint) { 619 final AccessPoint accessPoint = (AccessPoint) preference; 620 accessPoint.update(mLastInfo, mLastState); 621 } 622 } 623 624 if (mInXlSetupWizard) { 625 ((WifiSettingsForSetupWizardXL)getActivity()).updateConnectionState(mLastState); 626 } 627 } 628 629 private void updateWifiState(int state) { 630 getActivity().invalidateOptionsMenu(); 631 632 switch (state) { 633 case WifiManager.WIFI_STATE_ENABLED: 634 mScanner.resume(); 635 return; // not break, to avoid the call to pause() below 636 637 case WifiManager.WIFI_STATE_ENABLING: 638 addMessagePreference(R.string.wifi_starting); 639 break; 640 641 case WifiManager.WIFI_STATE_DISABLED: 642 addMessagePreference(R.string.wifi_empty_list_wifi_off); 643 break; 644 } 645 646 mLastInfo = null; 647 mLastState = null; 648 mScanner.pause(); 649 } 650 651 private class Scanner extends Handler { 652 private int mRetry = 0; 653 654 void resume() { 655 if (!hasMessages(0)) { 656 sendEmptyMessage(0); 657 } 658 } 659 660 void forceScan() { 661 removeMessages(0); 662 sendEmptyMessage(0); 663 } 664 665 void pause() { 666 mRetry = 0; 667 removeMessages(0); 668 } 669 670 @Override 671 public void handleMessage(Message message) { 672 if (mWifiManager.startScanActive()) { 673 mRetry = 0; 674 } else if (++mRetry >= 3) { 675 mRetry = 0; 676 Toast.makeText(getActivity(), R.string.wifi_fail_to_scan, 677 Toast.LENGTH_LONG).show(); 678 return; 679 } 680 sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); 681 } 682 } 683 684 private class WifiServiceHandler extends Handler { 685 686 @Override 687 public void handleMessage(Message msg) { 688 switch (msg.what) { 689 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 690 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 691 //AsyncChannel in msg.obj 692 } else { 693 //AsyncChannel set up failure, ignore 694 Log.e(TAG, "Failed to establish AsyncChannel connection"); 695 } 696 break; 697 case WifiManager.CMD_WPS_COMPLETED: 698 WpsResult result = (WpsResult) msg.obj; 699 if (result == null) break; 700 AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity()) 701 .setTitle(R.string.wifi_wps_setup_title) 702 .setPositiveButton(android.R.string.ok, null); 703 switch (result.status) { 704 case FAILURE: 705 dialog.setMessage(R.string.wifi_wps_failed); 706 dialog.show(); 707 break; 708 case IN_PROGRESS: 709 dialog.setMessage(R.string.wifi_wps_in_progress); 710 dialog.show(); 711 break; 712 default: 713 if (result.pin != null) { 714 dialog.setMessage(getResources().getString( 715 R.string.wifi_wps_pin_output, result.pin)); 716 dialog.show(); 717 } 718 break; 719 } 720 break; 721 //TODO: more connectivity feedback 722 default: 723 //Ignore 724 break; 725 } 726 } 727 } 728 729 /** 730 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 731 * Wifi setup screens, not in usual wifi settings screen. 732 * 733 * @param connected true when the device is connected to a wifi network. 734 */ 735 private void changeNextButtonState(boolean connected) { 736 if (mInXlSetupWizard) { 737 ((WifiSettingsForSetupWizardXL)getActivity()).changeNextButtonState(connected); 738 } else if (mEnableNextOnConnection && hasNextButton()) { 739 getNextButton().setEnabled(connected); 740 } 741 } 742 743 public void onClick(DialogInterface dialogInterface, int button) { 744 if (mInXlSetupWizard) { 745 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 746 forget(); 747 } else if (button == WifiDialog.BUTTON_SUBMIT) { 748 ((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed(); 749 } 750 } else { 751 if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { 752 forget(); 753 } else if (button == WifiDialog.BUTTON_SUBMIT) { 754 submit(mDialog.getController()); 755 } 756 } 757 758 } 759 760 /* package */ void submit(WifiConfigController configController) { 761 int networkSetup = configController.chosenNetworkSetupMethod(); 762 switch(networkSetup) { 763 case WifiConfigController.WPS_PBC: 764 case WifiConfigController.WPS_DISPLAY: 765 case WifiConfigController.WPS_KEYPAD: 766 mWifiManager.startWps(configController.getWpsConfig()); 767 break; 768 case WifiConfigController.MANUAL: 769 final WifiConfiguration config = configController.getConfig(); 770 771 if (config == null) { 772 if (mSelectedAccessPoint != null 773 && !requireKeyStore(mSelectedAccessPoint.getConfig()) 774 && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { 775 mWifiManager.connectNetwork(mSelectedAccessPoint.networkId); 776 } 777 } else if (config.networkId != INVALID_NETWORK_ID) { 778 if (mSelectedAccessPoint != null) { 779 saveNetwork(config); 780 } 781 } else { 782 if (configController.isEdit() || requireKeyStore(config)) { 783 saveNetwork(config); 784 } else { 785 mWifiManager.connectNetwork(config); 786 } 787 } 788 break; 789 } 790 791 if (mWifiManager.isWifiEnabled()) { 792 mScanner.resume(); 793 } 794 updateAccessPoints(); 795 } 796 797 private void saveNetwork(WifiConfiguration config) { 798 if (mInXlSetupWizard) { 799 ((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config); 800 } else { 801 mWifiManager.saveNetwork(config); 802 } 803 } 804 805 /* package */ void forget() { 806 mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId); 807 808 if (mWifiManager.isWifiEnabled()) { 809 mScanner.resume(); 810 } 811 updateAccessPoints(); 812 813 // We need to rename/replace "Next" button in wifi setup context. 814 changeNextButtonState(false); 815 } 816 817 /** 818 * Refreshes acccess points and ask Wifi module to scan networks again. 819 */ 820 /* package */ void refreshAccessPoints() { 821 if (mWifiManager.isWifiEnabled()) { 822 mScanner.resume(); 823 } 824 825 getPreferenceScreen().removeAll(); 826 } 827 828 /** 829 * Called when "add network" button is pressed. 830 */ 831 /* package */ void onAddNetworkPressed() { 832 // No exact access point is selected. 833 mSelectedAccessPoint = null; 834 showConfigUi(null, true); 835 } 836 837 /* package */ int getAccessPointsCount() { 838 final boolean wifiIsEnabled = mWifiManager.isWifiEnabled(); 839 if (wifiIsEnabled) { 840 return getPreferenceScreen().getPreferenceCount(); 841 } else { 842 return 0; 843 } 844 } 845 846 /** 847 * Requests wifi module to pause wifi scan. May be ignored when the module is disabled. 848 */ 849 /* package */ void pauseWifiScan() { 850 if (mWifiManager.isWifiEnabled()) { 851 mScanner.pause(); 852 } 853 } 854 855 /** 856 * Requests wifi module to resume wifi scan. May be ignored when the module is disabled. 857 */ 858 /* package */ void resumeWifiScan() { 859 if (mWifiManager.isWifiEnabled()) { 860 mScanner.resume(); 861 } 862 } 863} 864