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