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